交易策略“鹰或硬币”属于高风险短期交易方法,主要用于股票市场和外汇市场。其名称源于决策的随机性,类似于抛硬币(“鹰” - 购买资产,“硬币” - 出售资产)。这种策略仅基于直觉决策或随机信号,并忽略市场分析的基本因素。
代码库中已添加交易策略的源代码:
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块将保持为空。然而,即使是空块也很有用,因为它可以提醒开发者,如果将来需要,实现资源清理的可能性。
例如,如果您的专家处理图表、创建输出窗口或执行网络请求,关闭图表窗口、释放内存或终止连接将是合理的操作。
总结:
代码实现了在账户上没有活动头寸的情况下随机开仓的简单策略。主要关注点是检查资金可用性并确保能够以市场价格进行交易。这种方法可以用于测试目的或在不需要交易定向的系统中,优先考虑交易频率或其他因素。