You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MT5 EA开发问题:无报错但不执行,存在类型转换警告

MT5 EA 无执行问题排查与修复

问题描述

编写的MT5 EA需求为:仅当账户持仓处于盈利状态时开仓,持仓盈利时尽可能开立最大数量的仓位;持仓亏损则不开仓。目前代码无报错但完全不执行,仅存在“number隐式转换为string”的警告(testing.mq5第87行26列),原代码如下:

input double initialLotSize = 0.1; // Initial lot size
input int maxPositions = 100; // Maximum number of positions allowed
input double pipValue = 0.0001; // Pip value for the currency pair

double initialPrice = 0.0; // Price of the manually opened position
bool manualPositionOpened = false; // Flag to indicate if the manual position is opened
int totalPositions = 0; // Total number of positions

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   // Initialization code here
   return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   // Cleanup code here
}

//+------------------------------------------------------------------+
//| Calculate leverage based on account balance and margin           |
//+------------------------------------------------------------------+
double CalculateLeverage()
{
   double accountBalance = AccountInfoDouble(ACCOUNT_BALANCE);
   double accountMargin = AccountInfoDouble(ACCOUNT_MARGIN);

   if (accountMargin == 0.0)
      return 0.0; // To avoid division by zero

   return accountBalance / accountMargin;
}

//+------------------------------------------------------------------+
//| Check if leverage allows opening new positions                  |
//+------------------------------------------------------------------+
bool CanOpenPositions()
{
   double leverage = CalculateLeverage();
   return leverage > 1.0; // Allow opening positions if leverage is greater than 1
}

//+------------------------------------------------------------------+
//| Check if price moves 20 pips from the initial price             |
//+------------------------------------------------------------------+
bool PriceMoved20Pips()
{
   if (!manualPositionOpened)
      return false; // No manual position opened yet

   double currentPrice = SymbolInfoDouble(Symbol(), SYMBOL_BID);
   return MathAbs(currentPrice - initialPrice) >= 20 * pipValue; // Check if price moves 20 pips
}

//+------------------------------------------------------------------+
//| Open new positions based on criteria                             |
//+------------------------------------------------------------------+
void OpenNewPositions()
{
   // Check if there are any closed positions
   if (PositionsTotal() < totalPositions) {
      // If a position is closed, reset the flag
      manualPositionOpened = false;
   }

   // Update the totalPositions variable
   totalPositions = PositionsTotal();

   // Check if there are any open positions
   if (totalPositions == 0) {
      // If there are no open positions, reset the initial price and manualPositionOpened flag
      initialPrice = 0.0;
      manualPositionOpened = false;
      return;
   }

   // Calculate the total profit of all open positions
   double totalProfit = 0.0;
   for (int i = 0; i < totalPositions; ++i) {
      if (PositionSelect(i)) {
         totalProfit += PositionGetDouble(POSITION_PROFIT);
      }
   }

   // If total profit is negative or a manual position is not opened, do not open new positions
   if (totalProfit < 0 || !manualPositionOpened) {
      return;
   }

   // If a manual position is opened, update the initial price
   if (totalProfit > 0 && !manualPositionOpened) {
      initialPrice = SymbolInfoDouble(Symbol(), SYMBOL_BID);
      manualPositionOpened = true;
   }

   // Check if leverage allows opening new positions and if price hasn't moved 20 pips
   if (!CanOpenPositions() || PriceMoved20Pips()) {
      return;
   }

   // Calculate maximum number of positions based on leverage and balance
   int maxAllowedPositions = maxPositions - totalPositions;

   // Open new positions
   double lotSize = initialLotSize;
   for (int i = 0; i < maxAllowedPositions; ++i) {
      // Prepare request
      MqlTradeRequest request;
      ZeroMemory(request);
      request.action = TRADE_ACTION_DEAL;
      request.type = ORDER_TYPE_BUY;
      request.symbol = Symbol();
      request.volume = lotSize;
      request.price = SymbolInfoDouble(Symbol(), SYMBOL_ASK);

      // Send request
      MqlTradeResult result;
      if (!OrderSend(request, result)) {
         Print("Error opening order: ", GetLastError());
      }
   }
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   // Check account balance and leverage
   double accountBalance = AccountInfoDouble(ACCOUNT_BALANCE);
   double leverage = CalculateLeverage();

   // Calculate maximum number of positions based on leverage and balance
   int maxAllowedPositions = (int)(accountBalance / initialLotSize / leverage);

  // Print account information
Print("Account Balance: " + DoubleToString(accountBalance));
Print("Leverage: " + DoubleToString(leverage));
Print("Max Allowed Positions: " + IntegerToString(maxAllowedPositions));


   // Open new positions based on criteria
   OpenNewPositions();
}

问题根源

  1. 字符串拼接警告:MT5中不能直接用+连接字符串与转换后的数值,会触发隐式转换警告。
  2. 标志逻辑矛盾:manualPositionOpened的设置代码块被前置的return条件阻断,永远无法执行,导致该标志始终为false,直接触发开仓拦截。
  3. 持仓遍历错误:PositionSelect(i)无法正确选中持仓,需通过持仓单号PositionGetTicket(i)配合PositionSelectByTicket()遍历,导致总盈利计算错误。
  4. 开仓条件逻辑不合理:原CanOpenPositions用杠杆比值判断,无持仓时返回false,无法触发后续开仓;且未考虑可用保证金是否足够开新单。
  5. 初始持仓检测缺失:未处理手动开仓后的初始价格与标志设置,导致开仓逻辑一直被拦截。

修复方案

修复后的完整代码

input double initialLotSize = 0.1; // 初始仓位大小
input int maxPositions = 100; // 允许的最大持仓数
input double pipValue = 0.0001; // 货币对的点值

double initialPrice = 0.0; // 手动开仓的价格
bool manualPositionOpened = false; // 手动开仓标志
int totalPositions = 0; // 当前总持仓数

//+------------------------------------------------------------------+
//| 专家初始化函数                                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    // 初始化时检测是否已有手动开仓
    if(PositionsTotal() > 0)
    {
        long ticket = PositionGetTicket(0);
        if(PositionSelectByTicket(ticket))
        {
            initialPrice = PositionGetDouble(POSITION_PRICE_OPEN);
            manualPositionOpened = true;
            totalPositions = PositionsTotal();
        }
    }
    return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| 专家反初始化函数                                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    // 清理代码
}

//+------------------------------------------------------------------+
//| 计算单仓位所需保证金                                             |
//+------------------------------------------------------------------+
double CalculateMarginPerLot()
{
    double contractSize = SymbolInfoDouble(Symbol(), SYMBOL_TRADE_CONTRACT_SIZE);
    double price = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
    double leverage = AccountInfoDouble(ACCOUNT_LEVERAGE);
    
    return (contractSize * price) / leverage;
}

//+------------------------------------------------------------------+
//| 检查是否可以开新仓                                               |
//+------------------------------------------------------------------+
bool CanOpenPositions()
{
    double freeMargin = AccountInfoDouble(ACCOUNT_FREE_MARGIN);
    double marginPerLot = CalculateMarginPerLot();
    
    // 可用保证金足够开至少1手
    return freeMargin >= marginPerLot * initialLotSize;
}

//+------------------------------------------------------------------+
//| 检查价格是否偏离初始价格20点                                     |
//+------------------------------------------------------------------+
bool PriceMoved20Pips()
{
    if (!manualPositionOpened)
        return false;

    double currentPrice = SymbolInfoDouble(Symbol(), SYMBOL_BID);
    return MathAbs(currentPrice - initialPrice) >= 20 * pipValue;
}

//+------------------------------------------------------------------+
//| 根据条件开新仓                                                   |
//+------------------------------------------------------------------+
void OpenNewPositions()
{
    totalPositions = PositionsTotal();

    if (totalPositions == 0)
    {
        initialPrice = 0.0;
        manualPositionOpened = false;
        return;
    }

    // 计算总盈利
    double totalProfit = 0.0;
    for (int i = 0; i < totalPositions; ++i)
    {
        long ticket = PositionGetTicket(i);
        if (PositionSelectByTicket(ticket))
        {
            totalProfit += PositionGetDouble(POSITION_PROFIT);
        }
    }

    // 持仓亏损时不开仓
    if (totalProfit < 0)
    {
        return;
    }

    // 如果已有盈利且未设置初始价格,更新初始价格和标志
    if (totalProfit > 0 && !manualPositionOpened)
    {
        long ticket = PositionGetTicket(0);
        if(PositionSelectByTicket(ticket))
        {
            initialPrice = PositionGetDouble(POSITION_PRICE_OPEN);
            manualPositionOpened = true;
        }
    }

    // 无法开仓或价格偏离20点时返回
    if (!CanOpenPositions() || PriceMoved20Pips())
    {
        return;
    }

    // 计算可开最大仓位数量:取剩余仓位限制和可用保证金允许的最小值
    double freeMargin = AccountInfoDouble(ACCOUNT_FREE_MARGIN);
    double marginPerLot = CalculateMarginPerLot();
    int maxByMargin = (int)(freeMargin / (marginPerLot * initialLotSize));
    int maxAllowedPositions = MathMin(maxPositions - totalPositions, maxByMargin);
    
    if(maxAllowedPositions <= 0) return;

    // 批量开仓
    double lotSize = initialLotSize;
    for (int i = 0; i < maxAllowedPositions; ++i)
    {
        MqlTradeRequest request;
        ZeroMemory(request);
        request.action = TRADE_ACTION_DEAL;
        request.type = ORDER_TYPE_BUY;
        request.symbol = Symbol();
        request.volume = lotSize;
        request.price = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
        request.deviation = 10; // 添加滑点允许值
        request.magic = 12345; // 设置EA魔术号

        MqlTradeResult result;
        if (!OrderSend(request, result))
        {
            Print(StringFormat("开仓错误: %d", GetLastError()));
            break; // 开仓失败则停止后续尝试
        }
    }
}

//+------------------------------------------------------------------+
//| 专家Tick函数                                                     |
//+------------------------------------------------------------------+
void OnTick()
{
    double accountBalance = AccountInfoDouble(ACCOUNT_BALANCE);
    double freeMargin = AccountInfoDouble(ACCOUNT_FREE_MARGIN);
    int currentPositions = PositionsTotal();

    // 使用StringFormat避免隐式转换警告
    Print(StringFormat("账户余额: %.2f", accountBalance));
    Print(StringFormat("可用保证金: %.2f", freeMargin));
    Print(StringFormat("当前持仓数: %d", currentPositions));

    OpenNewPositions();
}

关键修复点说明

  • 解决字符串拼接警告:用StringFormat()替代+连接字符串与数值,避免隐式转换。
  • 修复标志逻辑:调整manualPositionOpened的判断顺序,确保盈利时能正确设置初始价格和标志;初始化时检测已有持仓,自动设置标志。
  • 正确遍历持仓:通过PositionGetTicket()获取持仓单号,配合PositionSelectByTicket()遍历,准确计算总盈利。
  • 优化开仓条件:改为检查可用保证金是否足够开单,替代原不合理的杠杆比值判断。
  • 合理计算开仓数量:结合最大持仓限制和可用保证金,取最小值作为可开仓位数量,避免爆仓风险。
  • 完善开仓请求:添加滑点允许值和魔术号,提升开仓成功率。

内容的提问来源于stack exchange,提问作者kim humphrey

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 11:12:03