正面或反面交易策略是对交易机器人代码的分析。

正面或反面交易策略是对交易机器人代码的分析。

21 一月 2026, 10:36
Vladimir Pastushak
0
9

交易策略“鹰或硬币”属于高风险短期交易方法,主要用于股票市场和外汇市场。其名称源于决策的随机性,类似于抛硬币(“鹰” - 购买资产,“硬币” - 出售资产)。这种策略仅基于直觉决策或随机信号,并忽略市场分析的基本因素。


代码库中已添加交易策略的源代码:

MetaTrader 5: https://www.mql5.com/zh/code/11637


#property copyright "Copyright 2025, Trading-Go." // Setting copyright property
#property link      "https://www.mql5.com/en/channels/tradingo-go-en"  // Setting developer resource link
#property version   "26.010" // Program version

上述代码是用于MetaTrader Expert Advisor (EA)或指标的编译器指令,用MQL4/MQL5语言编写。

让我们逐行分析:

1. #property copyright "Copyright 2025, Trading-Go."

此行设置了EA或指标源代码的法律所有权。它定义了版权所有者,并帮助标识产品归属于特定组织或个人(“Trading-Go”)。此信息显示在MetaTrader客户端终端的EA/指标属性窗口中。

2. #property link " "

此行允许设置开发者的网页资源链接。当交易者使用此EA或指标时,该链接将在工具属性中可用。这对开发者很有用,因为他们可以通过此方式将用户引导到支持页面、文档或与产品相关的社区。

3. #property version "26.010"

这里设置了软件版本。通常,开发者会以“XX.XX”的格式指定版本,其中第一个数字表示主要版本号,第二个数字表示次要版本号,第三个数字表示补丁。版本号帮助用户轻松跟踪更新并保持工具的兼容性。

#include 



 // Including Trade.mqh library
CTrade        trade; // Object of class CTrade to manage trades

#include 



 // Including PositionInfo.mqh library
CPositionInfo posit; // Object of class CPositionInfo to process positions

此代码块包含库并创建用于管理MetaTrader交易平台交易操作和头寸的类对象。

让我们详细分析每一行:

1. #include

这是包含头文件(Trade.mqh)的指令,其中包含CTrade类。该文件位于/Trade目录,表示它是MetaTrader提供的标准交易操作库的一部分。CTrade类用于方便地与交易进行交互,包括开仓、平仓、修改订单和获取交易状态信息。

2. CTrade trade;

创建一个名为trade的CTrade类的对象。该对象允许您管理交易的各个方面,如开新仓位、设置止损和止盈、平仓以及执行与交易过程相关的其他操作。

3. #include

这是包含第二个头文件(PositionInfo.mqh)的指令,也位于/Trade目录。该文件包含CPositionInfo类的定义,用于处理账户上当前开仓的信息。通过此类,可以获取每笔开仓的详细信息,如交易量、开仓价格、盈亏等。

4. CPositionInfo posit;

创建一个名为posit的CPositionInfo类的对象。使用此对象,可以查询您交易账户上所有活动头寸的详细信息,这有助于分析当前组合状态并根据所获信息做出决策。

代码块的总体目的:

此代码块为MetaTrader中的自动交易过程提供了基础,通过提供高级API(应用程序接口)。通过创建的trade和posit对象,可以实现自动化的交易决策、头寸监控和常规操作的自动化。

因此,这两个库可以简化复杂的自动交易算法的开发,使您能够专注于策略逻辑,而不是与经纪人服务器的低级交互。

input double  iLots         = 0.10; // Input parameter for lot size (trade volume)
input int     iTakeProfit   = 450;  // Input parameter for profit fixing level (Take Profit)
input int     iStopLoss     = 390;  // Input parameter for loss limitation level (Stop Loss)
input int     iMagicNumber  = 227; // Input parameter for unique deal number (Magic Number)
input int     iSlippage     = 30; // Input parameter for maximum price slippage

string sy = ""; // Variable to store instrument symbol
double pt = 0; // Variable for point calculation step
int    dt = 0; // Variable for decimal places count

让我们逐个分析上述代码块中的每个元素,解释其目的和在MetaTrader交易平台自动专家(Expert Advisor)配置过程中的作用。


输入参数(Input Parameters):

1. iLots = 0.10;

这是类型为double的输入参数,定义了手数(交易量)的大小。默认值设置为0.10。手数决定了每笔交易中购买或出售的资产数量。例如,如果工具是EURUSD,那么0.1手对应10,000单位的基础货币(例如,欧元)。

2. iTakeProfit = 450;

类型为int的参数,设置止盈(Take Profit)的水平。默认值为450。止盈自动关闭头寸,当市场价格达到相对于入场价格的指定盈利水平时。水平以点数(pips)表示。

3. iStopLoss = 390;

类型为int的参数,设置止损(Stop Loss)的水平。默认值为390。止损自动关闭头寸,当市场对您的头寸不利且损失达到指定水平时。损失以点数表示。

4. iMagicNumber = 227;

类型为int的参数,作为交易的唯一标识号(Magic Number)。每个事件(开仓、平仓等)都会获得一个唯一的Magic Number,允许根据此编号过滤交易。默认值为227。

5. iSlippage = 30;

类型为int的参数,限制订单执行价格与指定价格之间的最大偏差。设置的值为30点。如果实际价格与指定点数的差异超过此值,交易将不会执行。用于防止价格滑点过大。

本地变量(Local Variables):

1. sy = "";

类型为string的变量,用于存储金融工具的符号(例如,“EURUSD”),用于交易。初始为空字符串("")。

2. pt = 0;

类型为double的变量,用于存储特定工具的点数大小。用于根据点数计算止盈和止损水平。默认值为0。

3. dt = 0;

类型为int的变量,用于计算所选工具报价中的小数位数。小数位数对于正确计算盈利和止损以及其他指标很重要。初始值为0。

此代码块的目的是设置初始配置并为MetaTrader中的自动专家准备环境。输入参数块允许在交易开始前灵活配置EA的行为。

   sy = _Symbol; // Getting current trading instrument
   pt = _Point; // Getting minimum unit change size
   dt = _Digits; // Getting number of decimal digits in price

   trade.SetExpertMagicNumber(iMagicNumber); // Setting unique deal number for trade operations

   trade.SetDeviationInPoints(iSlippage); // Setting maximum price deviation points

   trade.SetTypeFillingBySymbol(sy); // Setting order execution type according to instrument settings

   trade.SetMarginMode(); // Setting margin mode


此代码块演示了在MetaTrader的自动专家(Expert Advisor)中初始化主要参数和配置交易操作。每个语句在为EA的成功运行准备条件方面都发挥着重要作用。

让我们详细分析每个表达式。

环境变量:

1. sy = _Symbol;

将全局变量sy赋值为当前交易工具的名称,EA使用的工具。_Symbol是内置常量,返回终端中选择的资产名称。这样,我们就可以访问用于后续计算和处理的工具名称。

2. pt = _Point;

设置变量pt,它接受工具的最小价格变化单位(_Point)。这是测量价格变化的基本单位,对于正确解释系统信号和调整订单很重要。

3. dt = _Digits;

定义变量dt,表示当前工具价格中的小数位数。由于不同的工具具有不同的小数位数(例如,USDJPY有两个小数位,EURUSD有四个),因此此变量对于正确的数字四舍五入和计算很重要。


配置交易对象:

4. trade.SetExpertMagicNumber(iMagicNumber);

为EA发起的所有交易设置唯一的魔术编号(Magic Number)。魔术编号用于标识由特定EA执行的交易,并简化根据此编号过滤头寸。编号的唯一性确保了在不同的机器人策略之间不会出现混淆。

5. trade.SetDeviationInPoints(iSlippage);

通过设置订单执行价格与预期价格之间的最大偏差(slippage),可以保护市场的大幅波动。设置点数值可以防止价格变化过大时执行交易。点数越小,您的请求执行得越准确。

6. trade.SetTypeFillingBySymbol(sy);

根据工具的特性配置订单执行类型(_Symbol)。一些工具需要即时执行(“Market Execution”),而其他工具可能允许延迟执行(“Instant Execution”)。此命令会根据符号的规格自动选择正确的模式。

7. trade.SetMarginMode();

配置EA的保证金模式。保证金管理方法因工具和交易模式而异。正确的保证金设置影响维持头寸所需的可用资金量,并最小化由于资金不足而强制平仓的风险。

   double stepvol = SymbolInfoDouble(sy, SYMBOL_VOLUME_STEP); // Get the lot size change step for the selected symbol
   if(stepvol > 0.0) // If the lot size step is positive, apply calculation of adjusted lot size
      lt = stepvol * (MathFloor(iLots / stepvol) - 1); // Round down the lot size to the nearest step value and decrease by one step
//---
   double minvol = SymbolInfoDouble(sy, SYMBOL_VOLUME_MIN); // Get minimum allowed lot size for the specified symbol
   if(lt < minvol) // If the adjusted lot size is less than the minimum possible value, reset it to zero
      lt = 0.0;

   ::MathSrand(GetTickCount()); // Generating initial number for random generator

上述代码块实现了对特定工具(货币、股票、合约等)的交易量(手数)的动态调整。主要目标是在交易所允许的范围内设置手数大小,避免在开仓时出错并确保交易安全。

让我们详细分析每一步:

1. 确定手数变化步长:

double stepvol = SymbolInfoDouble(sy, SYMBOL_VOLUME_STEP);

SymbolInfoDouble()命令获取所选符号的手数变化步长(SYMBOL_VOLUME_STEP)。步长定义了可以改变手数的最小间隔。例如,对于一些金融工具,步长为0.01,意味着交易只能以手数的整数百分之一进行。

2. 检查步长的正性:

if(stepvol > 0.0)

检查手数步长是否为正数。如果步长为零或负数,则后续计算没有意义。

3. 向下取整并减少手数:

lt = stepvol * (MathFloor(iLots / stepvol) - 1);

算法将交易者指定的交易量(iLots)减少一个最小步长。

以下是表达式内部的步骤:

iLots / stepvol: 计算适合输入手数的步长数量。

MathFloor(): 将小数值向下取整为最接近的整数。

减去一个单位:减少一个完整步长。

乘以步长:得到新的调整后的手数。

4. 获取最小允许的手数:

double minvol = SymbolInfoDouble(sy, SYMBOL_VOLUME_MIN);

命令获取所选符号的最小允许手数(SYMBOL_VOLUME_MIN)。交易所可能限制最小手数,低于此限制无法进行交易。

5. 检查最小边界:

if(lt < minvol)

   lt = 0.0;

如果调整后的手数小于交易所设置的最小值,则将其设置为零。这样可以防止机器人尝试以低于允许的最小手数下单。

6. 生成随机数生成器的初始值:

::MathSrand(GetTickCount());

生成初始值,用于后续使用随机数函数。命令获取当前的tick计数(GetTickCount())并用作后续伪随机序列的种子。这样可以增加随机过程结果的不可预测性。

   int total = ::PositionsTotal(), b = 0, s = 0; // Total open positions and purchase/sale counters

   double Bid = ::SymbolInfoDouble(sy, SYMBOL_BID); // Current bid price (selling price)
   double Ask = ::SymbolInfoDouble(sy, SYMBOL_ASK); // Current ask price (buying price)

   double new_sl = 0, new_tp = 0, old_sl = 0, old_tp = 0; // New and old stop loss and take profit levels

   if(Bid <= 0 || Ask <= 0) // If prices are incorrect
      return; // Exit function

上述代码块在MetaTrader的自动专家(Expert Advisor)中执行一系列重要的准备操作,以便执行进一步的交易逻辑。让我们详细分析每个阶段。

初始化变量:

1. 获取开仓总数:

int total = ::PositionsTotal();

PositionsTotal()函数返回账户上所有开仓的总数,无论方向和工具如何。这些数据对于后续的分析和可能的头寸优化很重要。

2. 初始化买卖计数器:

b = 0, s = 0;

变量b和s用于跟踪开仓的买入(buy)和卖出(sell)数量。它们初始为零,并在分析头寸时增加。


获取当前市场价格:

3. 请求BID价格(卖出价格):

Bid = ::SymbolInfoDouble(sy, SYMBOL_BID);

SymbolInfoDouble()函数返回当前工具(sy)的最新已知买入价格(BID)。BID价格反映了交易者可以立即出售工具的最佳市场价格。

4. 请求ASK价格(买入价格):

Ask = ::SymbolInfoDouble(sy, SYMBOL_ASK);

与上述相同,请求当前工具的最新已知卖出价格(ASK)。ASK价格显示了可以立即购买工具的最佳价格。

准备止损和止盈水平:

5. 声明新的和旧的止损和止盈水平:

new_sl = 0, new_tp = 0, old_sl = 0, old_tp = 0;

为未来的计算和比较止损和止盈水平进行预先初始化。零值表示在程序中后续逻辑发生之前没有设置水平。

安全性和价格正确性检查:

6. 检查价格的正确性:

if(Bid <= 0 || Ask <= 0)

   return;

执行简单的检查,确保价格正确。如果BID或ASK价格为零或负数,程序将退出当前过程(return),以避免错误操作。此条件保护系统免受由于不正确数据导致的不良后果。

 for(int i = 0; i < total; i++) // Loop through open positions
      if(posit.SelectByIndex(i)) // Select position by index
         if(posit.Symbol() == sy) // Check instrument of position
            if(posit.Magic() == iMagicNumber) // Check unique number of position
              {

               old_sl = ::NormalizeDouble(posit.StopLoss(),   dt); // Converting old stop loss to required decimal places
               old_tp = ::NormalizeDouble(posit.TakeProfit(), dt); // Converting old take profit to required decimal places

               if(posit.PositionType() == POSITION_TYPE_BUY) // If position is BUY (purchase)
                 {
                  new_sl = ::NormalizeDouble(Ask - iStopLoss   * pt, dt); // New stop loss below current ask price
                  new_tp = ::NormalizeDouble(Ask + iTakeProfit * pt, dt); // New take profit above current ask price
                  b++;                                                 // Increment purchases counter
                 }

               if(posit.PositionType() == POSITION_TYPE_SELL) // If position is SELL (sale)
                 {
                  new_sl = ::NormalizeDouble(Bid + iStopLoss   * pt, dt); // New stop loss above current bid price
                  new_tp = ::NormalizeDouble(Bid - iTakeProfit * pt, dt); // New take profit below current bid price
                  s++; // Increment sales counter
                 }

               if(old_sl == 0 || old_tp == 0) // If new levels differ from old ones
                  trade.PositionModify(posit.Ticket(), new_sl, new_tp);// Modify position with new SL and TP levels
              }

上述代码块实现了对账户上开仓的循环遍历,并更新相应头寸的止损(SL)和止盈(TP)水平。让我们依次分析每一个操作:


遍历开仓:

for(int i = 0; i < total; i++)

此处对开仓列表进行迭代。变量total已预先设置为账户上的开仓总数(PositionsTotal())。每个开仓都会被单独处理。


按索引选择头寸:

if(posit.SelectByIndex(i))

使用SelectByIndex()方法,根据索引号在列表中选择头寸。选择后,头寸对象可用于后续处理。


检查符号和魔术编号的匹配:

if(posit.Symbol() == sy && posit.Magic() == iMagicNumber)

首先检查所选头寸的交易工具是否与配置中的符号(sy)匹配。然后,将头寸的唯一编号(Magic Number)与专家参数(iMagicNumber)中设置的编号进行比较。这两个条件对于精确选择所需的头寸是必要的。


保存旧的SL和TP水平:

old_sl = NormalizeDouble(posit.StopLoss(), dt);

old_tp = NormalizeDouble(posit.TakeProfit(), dt);

对于所选头寸,保存现有的止损和止盈水平。重要的是,要根据工具的小数位数(dt)对获取的值进行规范化,以确保正确处理浮点数。


处理买入头寸(BUY):

if(posit.PositionType() == POSITION_TYPE_BUY)

如果所选头寸是买入(POSITION_TYPE_BUY),则执行以下块:

计算新的止损水平:新的止损线位于当前市场价格(Ask)以下,减去预先设置的止损水平(iStopLoss),以点数(pt)表示。

计算新的止盈水平:新的止盈水平位于当前市场价格(Ask)以上,加上预先设置的止盈水平(iTakeProfit),以点数表示。

统计:买入头寸计数器(b++)增加。


处理卖出头寸(SELL):

if(posit.PositionType() == POSITION_TYPE_SELL)

如果头寸是卖出(POSITION_TYPE_SELL),则执行以下块:

新的止损:位于当前价格(Bid)以上,加上止损水平。

新的止盈:位于当前价格(Bid)以下,减去止盈水平。

统计:卖出头寸计数器(s++)增加。


修改SL和TP水平:

if(old_sl == 0 || old_tp == 0)

   trade.PositionModify(posit.Ticket(), new_sl, new_tp);

如果旧的SL或TP水平不存在(等于零),则使用新的水平修改头寸。PositionModify()方法用于修改止损和止盈水平。

   if((b + s) == 0) // If there are no active positions
      if(::MathRand() % 2 == 0) // Randomly choosing direction of opening position
        {
         if(trade.CheckVolume(sy, lt, Ask, ORDER_TYPE_BUY)) // Verification of the sufficiency of funds to perform the required trading operation
            if(trade.Buy(lt)) // Opening long position (BUY)
               return; // End function execution
        }
      else
         if(trade.CheckVolume(sy, lt, Bid, ORDER_TYPE_SELL)) // Verification of the sufficiency of funds to perform the required trading operation
            if(trade.Sell(lt)) // Opening short position (SELL)
               return; // End function execution


上述代码块描述了在账户上没有活动头寸的情况下随机开仓(BUY或SELL)的算法。让我们详细分析过程:


检查活动头寸的存在:

if((b + s) == 0)

检查买入(b)和卖出(s)头寸计数器的总和。如果总和为零,则表示账户上没有活动头寸。只有在这种情况下,才会启动新头寸的开仓过程。


随机选择头寸方向:

if(MathRand() % 2 == 0)

随机选择未来头寸的方向。使用随机数生成器(MathRand())。% 2运算符返回随机数除以2的余数,给出50%的概率得到0(偶数随机数)和50%的概率得到非零值(奇数随机数)。


如果余数为零,则选择买入(ORDER_TYPE_BUY)。

否则,选择卖出(ORDER_TYPE_SELL)。

买入头寸(BUY)的开仓:

if(trade.CheckVolume(sy, lt, Ask, ORDER_TYPE_BUY))

在购买之前,使用CheckVolume()方法检查资金是否足够执行所需的交易。参数:

  • 工具(sy),
  • 手数(lt),
  • 当前买入价格(Ask),
  • 订单类型(ORDER_TYPE_BUY)。

该方法检查余额和资金可用性,以开仓所需的手数。

if(trade.Buy(lt))

如果资金足够,使用Buy()命令开仓,传递手数(lt)。如果操作成功,函数执行将被中止(return)。


卖出头寸(SELL)的开仓:

else

   if(trade.CheckVolume(sy, lt, Bid, ORDER_TYPE_SELL))

如果上述情况不成立(选择卖出),则检查资金是否足够卖出头寸:

  • 工具(sy),
  • 手数(lt),
  • 当前卖出价格(Bid),
  • 订单类型(ORDER_TYPE_SELL)。

if(trade.Sell(lt))

如果成功,使用Sell()命令创建卖出头寸,并停止执行(return)。


void OnDeinit(const int reason) // Deinitialization function
  {

  }

如果不需要特殊的清理操作,OnDeinit块将保持为空。然而,即使是空块也很有用,因为它可以提醒开发者,如果将来需要,实现资源清理的可能性。

例如,如果您的专家处理图表、创建输出窗口或执行网络请求,关闭图表窗口、释放内存或终止连接将是合理的操作。


总结:

代码实现了在账户上没有活动头寸的情况下随机开仓的简单策略。主要关注点是检查资金可用性并确保能够以市场价格进行交易。这种方法可以用于测试目的或在不需要交易定向的系统中,优先考虑交易频率或其他因素。