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

寻求改进MQL5账户本位币止损现金亏损计算EA的方案

问题:账户本位币现金亏损计算EA的交叉盘适配优化

我正在开发一款计算账户本位币现金亏损的EA,这是风控型多币种交易EA的核心模块。目前完成的初始代码仅在含USD的货币对(如EURUSD)上计算准确,非USD交叉对(如EURGBP、AUDJPY)的计算结果错误。后续计划添加NetLossPerOnePrecentLotPerPoint功能,但当前优先解决交叉盘计算逻辑问题,附上现有代码,求改进思路。

现有代码

#property copyright "Gerald Mann (P. Ghasemi)"
#property version   "1.00"

void OnTick()
{
   if (PositionsTotal() > 0)
   {
      double ContractSize = SymbolInfoDouble(Symbol(), SYMBOL_TRADE_CONTRACT_SIZE);
      // Check Handle
      if(!PositionSelect(_Symbol))
      {
         PrintFormat("PositionSelect(%s) failed. Error %d",_Symbol, GetLastError());
         return;
      }
      ResetLastError();
      long ticket=PositionGetInteger(POSITION_TICKET);
      //Check ticket
      if(ticket==0)
      {
         PrintFormat("Failed to get %s position ticket. Error %d", _Symbol, GetLastError());
         return;
      }
      // Position info
      double EntryPrice = PositionGetDouble(POSITION_PRICE_OPEN);
      double Price = PositionGetDouble(POSITION_PRICE_OPEN);
      double StopLossLevel = PositionGetDouble(POSITION_SL);
      double LotSize = PositionGetDouble(POSITION_VOLUME);
      
      //Calculations
      double Spent = LotSize * ContractSize;
      double Distance = MathAbs(Price - StopLossLevel);
      double PositionSlLossValueInCurrencyBaseValue = -(Spent * Distance);
      
      Print("EntryPrice:", EntryPrice);
      Print("StopLossLevel:", StopLossLevel);
      Print("Spent:", Spent);
      Print("PositionSlLossValueInCurrencyBaseValue:", PositionSlLossValueInCurrencyBaseValue);
   }
}

问题分析

现有代码的核心缺陷:

  • 计算出的PositionSlLossValueInCurrencyBaseValue是交易品种基准货币的亏损额,而非账户本位币
  • 仅当交易品种的基准货币与账户本位币一致(或通过USD间接关联)时结果看似正确,但交叉盘下基准货币与账户本位币无直接关联,缺少汇率转换步骤

改进思路与修正代码

要实现账户本位币的准确计算,需添加基准货币到账户本位币的汇率转换逻辑,步骤如下:

  1. 获取账户本位币
  2. 区分多空方向计算止损与入场价的点数差
  3. 优先用MT5内置接口获取已转换为账户本位币的点值,失败则手动计算交叉汇率
  4. 最终计算账户本位币的止损亏损额

修正后的代码:

#property copyright "Gerald Mann (P. Ghasemi)"
#property version   "1.01"

void OnTick()
{
   if (PositionsTotal() == 0) return;

   // 1. 获取账户与品种基础信息
   string accountCurrency = AccountInfoString(ACCOUNT_CURRENCY);
   string symbol = _Symbol;
   string baseCurrency = SymbolInfoString(symbol, SYMBOL_CURRENCY_BASE);
   double contractSize = SymbolInfoDouble(symbol, SYMBOL_TRADE_CONTRACT_SIZE);
   double point = SymbolInfoDouble(symbol, SYMBOL_POINT);

   // 2. 验证持仓有效性
   if(!PositionSelect(symbol))
   {
      PrintFormat("PositionSelect(%s) failed. Error %d", symbol, GetLastError());
      return;
   }
   long ticket = PositionGetInteger(POSITION_TICKET);
   if(ticket == 0)
   {
      PrintFormat("Failed to get %s position ticket. Error %d", symbol, GetLastError());
      return;
   }

   // 3. 获取持仓核心数据
   double entryPrice = PositionGetDouble(POSITION_PRICE_OPEN);
   double stopLoss = PositionGetDouble(POSITION_SL);
   double lotSize = PositionGetDouble(POSITION_VOLUME);
   ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);

   // 4. 计算止损点数差(区分多空方向)
   double pointDistance = 0.0;
   if(posType == POSITION_TYPE_BUY)
   {
      pointDistance = MathMax(0, entryPrice - stopLoss) / point;
   }
   else if(posType == POSITION_TYPE_SELL)
   {
      pointDistance = MathMax(0, stopLoss - entryPrice) / point;
   }
   if(pointDistance == 0)
   {
      Print("Stop loss not set or invalid.");
      return;
   }

   // 5. 计算每手每点的账户本位币价值
   double perLotPerPointValue = SymbolInfoDouble(symbol, SYMBOL_POINT_VALUE);
   // 内置接口失败时手动计算汇率转换
   if(perLotPerPointValue == 0)
   {
      string conversionSymbol = baseCurrency + accountCurrency;
      if(!SymbolSelect(conversionSymbol, true))
      {
         // 尝试反向货币对
         conversionSymbol = accountCurrency + baseCurrency;
         if(!SymbolSelect(conversionSymbol, true))
         {
            PrintFormat("Failed to find conversion symbol between %s and %s", baseCurrency, accountCurrency);
            return;
         }
         perLotPerPointValue = (contractSize * point) / SymbolInfoDouble(conversionSymbol, SYMBOL_BID);
      }
      else
      {
         perLotPerPointValue = contractSize * point * SymbolInfoDouble(conversionSymbol, SYMBOL_BID);
      }
   }

   // 6. 计算账户本位币的止损亏损额
   double accountLoss = -(lotSize * pointDistance * perLotPerPointValue);

   // 输出结果
   Print("Entry Price: ", entryPrice);
   Print("Stop Loss: ", stopLoss);
   Print("Lot Size: ", lotSize);
   Print("Point Distance: ", pointDistance);
   Print("Account Currency Loss: ", accountLoss);
}

关键优化点

  • 区分多空方向计算点数差,避免反向止损的错误计算
  • 优先使用MT5内置的SYMBOL_POINT_VALUE,保证点值转换的准确性
  • 内置接口失败时自动尝试直接/反向货币对获取交叉汇率,覆盖所有币种组合
  • 增加止损有效性判断,避免无止损时的无效计算

内容的提问来源于stack exchange,提问作者Peiman Ghasemi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 13:40:02