You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

EA策略测试中Breakeven函数出现Invalid stops错误的解决求助

解决EA策略测试中重复修改仓位的[Invalid stops]错误

问题分析

你遇到的Invalid stops错误本质是重复尝试将止损设置为当前已经存在的价格。从日志信息能看到,你尝试修改的止损/止盈和当前完全一致:sl: 1.14730, tp: 1.13730 -> sl: 1.14730, tp: 1.13730,MT4/MT5不允许这种无意义的重复修改操作,所以返回错误。

放慢测试速度后功能正常但仍报错,是因为即使保本已经生效,你的代码还是会在后续的tick中反复触发修改请求——只要价格满足price>=(PriceEntry+ExtBreakEven)(多单)或price<=(PriceEntry-ExtBreakEven)(空单)的条件,就会执行修改,完全没检查当前止损是否已经是保本价。

解决方案

我们需要给代码增加止损状态检查,只有当当前止损还未设置为保本价时,才执行修改操作。同时优化价格比较的精度,避免因小数点位数问题导致的误触发。

修改后的完整代码片段

double break_even = 3.0;
double ExtBreakEven = 0.0;
double m_adjusted_point; // 确保这个变量全局声明
input ulong magic_number = 12345; // 替换为你实际使用的魔术号码

int OnInit()
{
    //--- 适配3/4/5位小数的货币对
    int digits_adjust=1;
    if(_symbol.Digits()==3 || _symbol.Digits()==4 || _symbol.Digits()==5)
        digits_adjust=10;
    m_adjusted_point=_symbol.Point()*digits_adjust;
    //---
    ExtBreakEven = break_even * m_adjusted_point;
    
    return(INIT_SUCCEEDED); // 补充OnInit的规范返回值
}

//..... 保本逻辑实现
void BreakEven(const double price)
{
    if (break_even==0) return;
    for (int i=PositionsTotal()-1; i>=0; i--){
        string symbol=PositionGetSymbol(i);
        ulong magic=PositionGetInteger(POSITION_MAGIC);
        if (symbol==_symbol.Name() && magic == magic_number){
            ulong PositionTicket=PositionGetInteger(POSITION_TICKET);
            double PriceEntry=PositionGetDouble(POSITION_PRICE_OPEN);
            double CurrentStopLoss=PositionGetDouble(POSITION_SL); // 新增:获取当前止损价
            double CurrentTakeProfit=PositionGetDouble(POSITION_TP);
            
            if (PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY){
                // 双重检查:价格达标 + 当前止损未到保本价(兼容精度误差)
                if (price>=(PriceEntry+ExtBreakEven) && MathAbs(CurrentStopLoss - PriceEntry) > m_adjusted_point*0.1){
                    if (!_trade.PositionModify(PositionTicket,PriceEntry,CurrentTakeProfit))
                    {
                        Print("Modify ",PositionTicket, " Position -> false. Result Retcode: ",_trade.ResultRetcode(), ", description of result: ",_trade.ResultRetcodeDescription());
                        PrintResultModify(_trade,_symbol,_position);
                    }
                }
            }
            else if (PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL){
                // 空单同样增加止损状态检查
                if (price<=(PriceEntry-ExtBreakEven) && MathAbs(CurrentStopLoss - PriceEntry) > m_adjusted_point*0.1){
                    if (!_trade.PositionModify(PositionTicket,PriceEntry,CurrentTakeProfit)){
                        Print("Modify ",PositionTicket, " Position -> false. Result Retcode: ",_trade.ResultRetcode(), ", description of result: ",_trade.ResultRetcodeDescription());
                        PrintResultModify(_trade,_symbol,_position);
                    }
                }
            }
        }
    }
}

修改要点说明

  1. 新增止损状态检查:获取当前仓位的POSITION_SL值,和目标保本价(PriceEntry)做精度兼容的比较——只有当两者差值大于m_adjusted_point*0.1(极小阈值,避免小数点精度误差)时,才执行修改操作,彻底杜绝重复请求。
  2. 简化空单判断逻辑:把原代码中冗余的else嵌套if结构改为else if,让代码逻辑更简洁易读。
  3. 补全规范代码:给OnInit函数补充了必要的返回值,确保符合MQL5的代码规范;同时明确了全局变量的声明,避免函数间变量访问异常。
  4. 精度兼容处理:用MathAbs计算价格差值,配合自定义的点值阈值,避免因不同货币对的小数位数差异导致的误判。

额外调试建议

如果需要进一步排查触发逻辑,可以在BreakEven函数开头添加日志输出,跟踪每次触发的价格和条件:

Print("BreakEven triggered | Current Price: ", price, " | Entry Price: ", PriceEntry, " | Current SL: ", CurrentStopLoss);

内容的提问来源于stack exchange,提问作者Dragan Drenjanin

火山引擎 最新活动