EA运行报错排查实录 - 06月27日更新
兄弟们,今天花了一整个下午在调试一个EA,报错信息是“OrderSend error 130”,也就是无效止损止盈。这个错误看起来简单,但坑其实挺深的,我一步步拆解一下排查过程,希望对大家有帮助。
先交代背景:EA是基于日线均线交叉开单的,我用的是MQL5,但逻辑移植到MQL4也基本相通。报错发生在实盘模拟测试中,回测一切正常,一挂到模拟账户就频繁报130。我最初怀疑是小数点差异,但检查后发现,止损位计算用的是NormalizeDouble,并且参考了点值。可模拟账户的经纪商是ECN模式,允许负点差,这可能是关键点。
第一步,我打印了OrderSend函数传入的所有参数,包括价格、止损、止盈、滑点。发现止损价居然低于当前Ask,而且差值小于1个点。这本来不应该,因为止损逻辑是“入场价减去ATR*2”,按理说ATR算出来足够大。但问题出在ATR指标在初始化阶段没加载足够数据,导致前几根K线ATR值异常小。我加了一个判断:如果ATR < 真实点数*5,就跳过开单,或者强制用最小止损距离替代。
第二步,检查了止损距离与经纪商限制。不同经纪商对止损最小距离有不同要求,有的强制10点、20点。我加了MarketInfo(Symbol(), MODE_STOPLEVEL)动态获取,然后计算止损偏移时,确保大于这个值。但注意,STOPLEVEL返回的是点,不是点值,需要先除以点。我踩过这个坑,之前直接当点数用,结果差10倍。
第三步,考虑市价单和挂单的区别。我的EA用市价单,但有些经纪商对市价单的止损止盈有额外限制,比如必须在开单后修改。我改成先开单,再用OrderModify单独设置止损止盈,配合Sleep(50)等待订单确认。但Sleep在回测中会失效,所以得用循环检测订单状态。
第四步,滑点问题。我设滑点为10点,但ECN账户滑点可能触发重新报价,导致OrderSend返回error 138。我加了重试机制,最多3次,每次间隔500ms,并且滑点动态调整成当前市场波动率的倍数。
最后,测试时发现,如果同时开多个品种,线程冲突会导致全局变量被覆盖。我改成每个品种用独立的ChartID和窗口句柄,或者用OrderSendAsync异步模式,但MQL4不支持,只好用临界区或互斥锁。
附上关键代码片段:
int slippage = (int)(MarketInfo(sym, MODE_SPREAD) * 1.5);
double stopLoss = NormalizeDouble(entryPrice - (ATR * 2), digits);
double takeProfit = NormalizeDouble(entryPrice + (ATR * 3), digits);
double minStop = MarketInfo(sym, MODE_STOPLEVEL) * Point;
if (MathAbs(stopLoss - entryPrice) < minStop) {
if (orderType == OP_BUY) stopLoss = entryPrice - minStop;
else stopLoss = entryPrice + minStop;
}
if (!OrderSend(sym, orderType, lot, entryPrice, slippage, stopLoss, takeProfit, comment, magic, 0, clr)) {
Print("OrderSend failed: ", GetLastError());
// 重试逻辑
}
总结:130错误90%是止损止盈数值问题,但根源可能是经纪商规则、数据初始化、或市场动态。别只盯着代码,先把经纪商的交易规则文档翻一遍。希望这个实录能帮到还在踩坑的朋友,大家有类似经历欢迎补充。
兄弟们,今天花了一整个下午在调试一个EA,报错信息是“OrderSend error 130”,也就是无效止损止盈。这个错误看起来简单,但坑其实挺深的,我一步步拆解一下排查过程,希望对大家有帮助。
先交代背景:EA是基于日线均线交叉开单的,我用的是MQL5,但逻辑移植到MQL4也基本相通。报错发生在实盘模拟测试中,回测一切正常,一挂到模拟账户就频繁报130。我最初怀疑是小数点差异,但检查后发现,止损位计算用的是NormalizeDouble,并且参考了点值。可模拟账户的经纪商是ECN模式,允许负点差,这可能是关键点。
第一步,我打印了OrderSend函数传入的所有参数,包括价格、止损、止盈、滑点。发现止损价居然低于当前Ask,而且差值小于1个点。这本来不应该,因为止损逻辑是“入场价减去ATR*2”,按理说ATR算出来足够大。但问题出在ATR指标在初始化阶段没加载足够数据,导致前几根K线ATR值异常小。我加了一个判断:如果ATR < 真实点数*5,就跳过开单,或者强制用最小止损距离替代。
第二步,检查了止损距离与经纪商限制。不同经纪商对止损最小距离有不同要求,有的强制10点、20点。我加了MarketInfo(Symbol(), MODE_STOPLEVEL)动态获取,然后计算止损偏移时,确保大于这个值。但注意,STOPLEVEL返回的是点,不是点值,需要先除以点。我踩过这个坑,之前直接当点数用,结果差10倍。
第三步,考虑市价单和挂单的区别。我的EA用市价单,但有些经纪商对市价单的止损止盈有额外限制,比如必须在开单后修改。我改成先开单,再用OrderModify单独设置止损止盈,配合Sleep(50)等待订单确认。但Sleep在回测中会失效,所以得用循环检测订单状态。
第四步,滑点问题。我设滑点为10点,但ECN账户滑点可能触发重新报价,导致OrderSend返回error 138。我加了重试机制,最多3次,每次间隔500ms,并且滑点动态调整成当前市场波动率的倍数。
最后,测试时发现,如果同时开多个品种,线程冲突会导致全局变量被覆盖。我改成每个品种用独立的ChartID和窗口句柄,或者用OrderSendAsync异步模式,但MQL4不支持,只好用临界区或互斥锁。
附上关键代码片段:
int slippage = (int)(MarketInfo(sym, MODE_SPREAD) * 1.5);
double stopLoss = NormalizeDouble(entryPrice - (ATR * 2), digits);
double takeProfit = NormalizeDouble(entryPrice + (ATR * 3), digits);
double minStop = MarketInfo(sym, MODE_STOPLEVEL) * Point;
if (MathAbs(stopLoss - entryPrice) < minStop) {
if (orderType == OP_BUY) stopLoss = entryPrice - minStop;
else stopLoss = entryPrice + minStop;
}
if (!OrderSend(sym, orderType, lot, entryPrice, slippage, stopLoss, takeProfit, comment, magic, 0, clr)) {
Print("OrderSend failed: ", GetLastError());
// 重试逻辑
}
总结:130错误90%是止损止盈数值问题,但根源可能是经纪商规则、数据初始化、或市场动态。别只盯着代码,先把经纪商的交易规则文档翻一遍。希望这个实录能帮到还在踩坑的朋友,大家有类似经历欢迎补充。
专注交易策略编程实现,分享MQL开发技巧与代码优化方案