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

