MQL5 EA测试报错‘Failed to get position ticket’求助
解决MQL5 EA中"Failed to get position ticket"错误
错误根源
你遇到的Failed to get position ticket错误,核心问题出在仓位遍历的逻辑和错误处理上:
- 遍历仓位时直接依赖
PositionsTotal()的实时值作为循环上限,而遍历过程中仓位可能被关闭或新增,导致索引i失效,PositionGetTicket(i)返回0 - 错误处理过于激进,遇到单个无效仓位就直接终止整个统计/平仓流程,而不是跳过无效项继续执行
修复步骤
1. 修正仓位遍历的错误处理逻辑
将CountOpenPositions和ClosePositions函数中,遇到无效ticket的处理从return false改为continue,跳过无效仓位继续遍历:
修改后的CountOpenPositions函数
bool CountOpenPositions(int &cntBuy, int &cntSell){ cntBuy = 0; cntSell = 0; int total = PositionsTotal(); // 先固定总数,避免遍历中仓位数量变化导致索引失效 for(int i=total-1; i>=0; i--){ ulong ticket = PositionGetTicket(i); if(ticket<=0){ Print("跳过无效仓位,索引:",i); continue; // 跳过无效项,不终止函数 } if(!PositionSelectByTicket(ticket)){ Print("无法选中仓位,ticket:",ticket); continue; } long magic; if(!PositionGetInteger(POSITION_MAGIC,magic)){ Print("无法获取仓位魔术号,ticket:",ticket); continue; } if(magic==InpMagicNumber){ long type; if(!PositionGetInteger(POSITION_TYPE,type)){ Print("无法获取仓位类型,ticket:",ticket); continue; } if(type==POSITION_TYPE_BUY){cntBuy++;} if(type==POSITION_TYPE_SELL){cntSell++;} } } return true; }
修改后的ClosePositions函数
bool ClosePositions(int all_buy_sell){ int total = PositionsTotal(); for(int i=total-1; i>=0; i--){ ulong ticket = PositionGetTicket(i); if(ticket<=0){ Print("跳过无效仓位,索引:",i); continue; } if(!PositionSelectByTicket(ticket)){ Print("无法选中仓位,ticket:",ticket); continue; } long magic; if(!PositionGetInteger(POSITION_MAGIC,magic)){ Print("无法获取仓位魔术号,ticket:",ticket); continue; } if(magic==InpMagicNumber){ long type; if(!PositionGetInteger(POSITION_TYPE, type)){ Print("无法获取仓位类型,ticket:",ticket); continue; } if(all_buy_sell==1 && type==POSITION_TYPE_SELL){continue;} if(all_buy_sell==2 && type==POSITION_TYPE_BUY) {continue;} trade.PositionClose(ticket); if(trade.ResultRetcode()!=TRADE_RETCODE_DONE){ Print("平仓失败,ticket:",(string)ticket," 错误码:",(string)trade.ResultRetcode(),":",trade.CheckResultRetcodeDescription()); } } } return true; }
2. 修复CheckInputs函数的逻辑漏洞
原函数中即使参数错误也会返回true,导致OnInit无法拦截无效参数,修改为:
bool CheckInputs(){ bool isValid = true; if(InpMagicNumber<=0){ Alert("参数错误:魔术号不能<=0"); isValid = false; } if(InpLot<=0){ Alert("参数错误:手数不能<=0"); isValid = false; } if(InpBars<=0){ Alert("参数错误:周期数不能<=0"); isValid = false; } if(InpStopLoss<0){ Alert("参数错误:止损不能<0"); isValid = false; } if(InpTakeProfit<0){ Alert("参数错误:止盈不能<0"); isValid = false; } return isValid; }
3. 额外优化:初始化高低价
在OnInit中提前计算一次高低价,避免OnTick前几次执行时high/low为0的情况:
int OnInit() { if(!CheckInputs()) { return INIT_PARAMETERS_INCORRECT;} trade.SetExpertMagicNumber(InpMagicNumber); // 提前计算初始高低价 high=iHigh(_Symbol,PERIOD_CURRENT,iHighest(_Symbol,PERIOD_CURRENT,MODE_HIGH,InpBars,0)); low=iLow(_Symbol,PERIOD_CURRENT,iLowest(_Symbol,PERIOD_CURRENT,MODE_LOW,InpBars,0)); return(INIT_SUCCEEDED); }
总结
- 仓位遍历要先固定总数,避免遍历过程中仓位数量变化导致索引失效;对单个无效仓位做跳过处理,不要终止整个流程
- 确保参数校验函数能正确返回校验结果,拦截无效参数
- 提前初始化关键变量,避免逻辑判断异常
内容的提问来源于stack exchange,提问作者marcq
相关产品推荐
相关产品推荐

