MT5测试器分析仓位属性实战
用MQL5 Cookbook案例讲透EA回测与仓位监控
文章背景与学习目标
在MQL5 Cookbook系列的前一篇文章中,我们已经创建了一个在自定义信息面板上显示当前交易品种仓位属性的EA。本文在此基础上进行修改,重点解决几个实战中必然遇到的问题:如何在当前交易品种中检查新柱事件、如何从柱中获取数据、如何在文件中包含标准库中的交易类、如何创建函数搜索交易信号、如何创建函数执行交易操作,以及如何在OnTrade函数中判断交易事件。作者刻意使用极简的例子展示这些特性,把每个任务都封装进简单直接的函数中,便于初学者理解。后续文章会逐渐让这些函数复杂化以适应新需求。
之所以要修改而非重写,是因为我们需要保留之前EA的所有面板显示功能,只在逻辑层加入自动交易与测试支持。对于中文交易者来说,这种渐进式学习路径比一次性灌输复杂框架更友好,也能避免在刚接触MQL5时被标准库内部结构劝退。
引入标准库交易类CTrade
MQL5标准库提供了封装好的交易类,其中最常用的是CTrade。它位于终端目录的MQL5\Include\Trade\Trade.mqh,内部实现了开仓、平仓、修改止损获利等所有基础交易操作函数。我们无需一开始就去研读它的源码,直接包含并使用即可。在EA文件开头(例如#define之后)写入包含指令,再声明一个类实例,就能通过实例指针调用方法。
//--- 包含标准库中的一个类 #include <Trade/Trade.mqh> //--- 载入类 CTrade trade;
本文版本中,我们只用到CTrade的PositionOpen()函数。该函数不仅能建立新仓,还能在已有反向持仓时追加仓位实现净头寸变化(需注意文中示例在反向时以原仓位量+初始手数开仓,实际是加仓而非严格反向平仓,后续可优化)。当需要调用类方法时,输入实例名加小数点,IDE会弹出方法列表供选择,这对不熟悉参数顺序的新手非常实用。
全局数组与新柱检测函数
为了保存价格数据,我们在全局范围声明了两个动态数组close_price和open_price,用于接收柱的收盘价与开盘价。接着是CheckNewBar()函数,它的职责是判断当前是否出现了新柱,从而确保交易行为只针对已完成的柱执行,避免在柱内波动频繁触发信号。
//+------------------------------------------------------------------+
//| 检查新柱 |
//+------------------------------------------------------------------+
bool CheckNewBar()
{
static datetime new_bar=NULL;
static datetime time_last_bar[1]={0};
if(CopyTime(_Symbol,Period(),0,1,time_last_bar)==-1)
{ Print(__FUNCTION__,": 复制柱开启时间出错: "+IntegerToString(GetLastError())+""); }
if(new_bar==NULL)
{
new_bar=time_last_bar[0];
Print(__FUNCTION__,": 初始化 ["+_Symbol+"][TF: "+TimeframeToString(Period())+"]["
+TimeToString(time_last_bar[0],TIME_DATE|TIME_MINUTES|TIME_SECONDS)+"]");
return(false);
}
if(new_bar!=time_last_bar[0])
{
new_bar=time_last_bar[0];
return(true);
}
return(false);
}
这里使用了静态局部变量new_bar和time_last_bar。静态变量在函数退出后依然保持值,下次调用时延续上一次的状态。第一次调用时new_bar为NULL,函数记录当前柱时间并返回false;之后若柱时间变化,说明新柱形成,更新时间并返回true。CopyTime()负责读取最新柱开盘时间,建议查阅MQL5参考了解其参数。另外用到的TimeframeToString()把周期枚举转为可读字符串,方便日志阅读。
获取历史柱数据的方法
GetBarsData()函数从一定数量的柱中取得数据。我们设定amount=2,即取当前柱与前一柱。通过ArraySetAsSeries(true)将数组设为时间序列倒序,使索引0永远是最新柱,索引1是前一柱。这样在计算时写open_price[0]即代表当前开盘价,非常直观。CopyClose与CopyOpen分别填充收盘与开盘数组,若复制数量不足则打印错误。
void GetBarsData()
{
int amount=2;
ArraySetAsSeries(close_price,true);
ArraySetAsSeries(open_price,true);
if(CopyClose(_Symbol,Period(),0,amount,close_price)<amount)
{ Print("复制数据到收盘价数组失败"); }
if(CopyOpen(_Symbol,Period(),0,amount,open_price)<amount)
{ Print("复制数据到开盘价数组失败"); }
}
类似地,CopyHigh和CopyLow可取得最高最低价。这种数组处理方式与CheckNewBar中CopyTime的用法一脉相承,是MQL5中获取历史报价的基石。中文交易者常犯的错误是忘记设置ArraySetAsSeries,导致索引方向反掉,用错价格。
基于柱形的简易信号判断
为演示信号生成,我们用已完成柱(索引1)的收盘与开盘关系判断方向:若收盘价高于开盘价,为牛市柱,返回买入信号0;若低于,为熊市柱,返回卖出信号1;异常时返回3。实际策略可替换为复杂条件,但函数骨架不变。
int GetTradingSignal()
{
if(close_price[1]>open_price[1]) return(0);
if(close_price[1]<open_price[1]) return(1);
return(3);
}
注意这里用的是close_price[1]而非[0],因为[0]是当前未闭合柱,其收盘会变化,用作信号会导致重绘与不确定成交。只用已关闭的前一柱,是回测与实盘一致性的关键。
交易执行模块TradingBlock
TradingBlock()整合信号与下单。它先取信号,用PositionSelect查当前品种有无持仓。买入信号下读取规范化后的买价,若无仓则trade.PositionOpen开多;若有空仓则计算lot=原量+初始量后开多(示例为加仓)。卖出对称。所有价格必须用NormalizeDouble按_digits规范化,否则下单报错。止损获利暂传0,后续文章详解。
void TradingBlock()
{
int signal=-1;
double start_lot=0.1;
double ask=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
signal=GetTradingSignal();
if(signal==0)
{
if(!PositionSelect(_Symbol))
trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,start_lot,ask,0,0,"hello :)");
}
}
调用类方法时,实例名后加点再选函数,如trade.PositionOpen。参数依次为品种、类型、手数、价格、止损、获利、注释。若开启失败,GetLastError配合ErrorDescription打印便于排查。此模块展示了MQL5面向对象交易调用的极简形态。
事件函数与可视化测试安排
在OnInit中调用CheckNewBar初始化,OnTick中先判新柱,是新柱才取数据并交易,最后刷新面板。OnTrade则在任何成交事件后立刻更新面板,满足人工干预时即时反馈。策略测试器开启可视化模式可逐步观察,F12单步,仅开盘价模式一步一柱,每一订单模式一步一单。
void OnTick()
{
if(!CheckNewBar()) return;
GetBarsData();
TradingBlock();
GetPositionProperties();
}
void OnTrade()
{
GetPositionProperties();
}
为适配可视化测试,面板纵坐标在MQL5_VISUAL_MODE下调整,因测试器不会像实盘那样在右上角显示EA名,避免遮挡。信息面板名称数组pos_prop_texts配合循环创建标签,减少重复代码,是GUI模块化雏形。
常见坑与对比小结
对比社区中另一种新柱写法:用static datetime PrevBars=iTime获取时间比较,本质与CheckNewBar相同,但后者封装更好。常见坑包括:未ArraySetAsSeries导致用错柱;价格未NormalizeDouble导致成交拒绝;在OnTick直接用当前柱信号引起未来函数;忽略OnTrade致面板不实时。相对于纯脚本读属性,本文EA在测试器中自动跑并面板监控,更贴近真实策略开发流程。
总结来说,本文以最小可用代码串起回测EA核心链路:新柱检测、数据提取、信号、交易、事件刷新。读者可照此框架替换信号逻辑,逐步加入止损与资金管理,构建自己的系统。原文代码见 https://www.mql5.com/zh/articles/642 附加的positionpropertiestesteren.mq5。