我们在前面讨论计算 PipPoint
和 GetSlippage
时用到了 MarketInfo()
函数来获取交易品种的 Point
值(最小价格变动单位)和报价小数位数 (Digits
)。MarketInfo()
是一个非常有用的函数,在MQL4编程中会经常使用它来获取各种必要的市场实时或历史信息。
其函数语法如下:
double MarketInfo(string symbol, int type);
symbol
(字符串类型): 指定想查询哪个交易品种的信息。- 若要查询当前 EA/指标所在图表的品种信息,可以直接使用内置函数
Symbol()
。 - 若要查询其他品种(例如,进行跨品种分析或交易),则需要明确提供该品种的名称字符串,如
"EURJPY"
。
- 若要查询当前 EA/指标所在图表的品种信息,可以直接使用内置函数
type
(整数类型): 通过传入预定义的常量,指定您想获取哪一类信息。该函数的返回值类型是double
,但根据请求的type
不同,返回值的实际含义也不同(有时需要强制转换为int
或datetime
)。
MarketInfo() 参数中常用的常量 (type
): (完整列表请查阅官方参考文档 -> 标准常量 -> 市场信息)
MODE_POINT
: 获取该品种价格的最小变动单位。例如,EURUSD (5位报价) 返回 0.00001,USDJPY (3位报价) 返回 0.001。MODE_DIGITS
: 获取该品种报价的小数位数。例如,EURUSD 返回 5,USDJPY 返回 3。MODE_SPREAD
: 获取当前的实时点差。注意:返回值单位是points (即Point
的整数倍)。例如,若点差为 3 pips,对于 5 位报价的 EURUSD,此模式返回 30;对于 3 位报价的 USDJPY,也返回 30。MODE_STOPLEVEL
: 获取经纪商设置的最小止损和挂单距离,单位也是points。您设置的止损价或挂单价与当前市价的距离必须大于等于这个值。例如,若最小距离为 3 pips,对于 5 位报价的 EURUSD,此模式返回 30。MODE_BID
: 获取指定品种当前的买价 (Bid)。MODE_ASK
: 获取指定品种当前的卖价 (Ask)。MODE_LOW
: 获取指定品种当前 K 线 (Bar 0,即正在形成的 K 线) 的最低价 (Low)。MODE_HIGH
: 获取指定品种当前 K 线 (Bar 0) 的最高价 (High)。
基于前面讨论并计算得到的UsePoint
(代表 1 pip 的价格值),我们可以精确地计算止损价格。回顾一下基本原则:买单止损在开仓价之下,卖单止损在开仓价之上。
买单止损计算 (基于 StopLossPips
点数):
double openPrice = Ask; // 市价买单开仓价
double buyStopLossPrice = 0.0;
if (StopLossPips > 0)
{
// 止损价格 = 开仓价 - (止损点数 * 每 pip 的价格值)
buyStopLossPrice = openPrice - (StopLossPips * UsePoint);
// 规范化价格,确保符合精度要求
buyStopLossPrice = NormalizeDouble(buyStopLossPrice, Digits);
}
卖单止损计算 (基于 StopLossPips
点数):
double openPrice = Bid; // 市价卖单开仓价
double sellStopLossPrice = 0.0;
if (StopLossPips > 0)
{
// 止损价格 = 开仓价 + (止损点数 * 每 pip 的价格值)
sellStopLossPrice = openPrice + (StopLossPips * UsePoint);
// 规范化价格
sellStopLossPrice = NormalizeDouble(sellStopLossPrice, Digits);
}
对于挂单,计算逻辑完全相同,只需将 openPrice
替换为您的挂单价格即可。
计算止盈价格
计算止盈价格 (Take Profit, TP) 的逻辑与止损类似,只是加减方向相反:
- 买单:止盈价 = 开仓价 + (止盈点数 * 每 pip 的价格值)
- 卖单:止盈价 = 开仓价 – (止盈点数 * 每 pip 的价格值)
// 买单止盈计算 (假设 TakeProfitPips 为止盈点数)
double buyTakeProfitPrice = 0.0;
if (TakeProfitPips > 0)
{
buyTakeProfitPrice = openPrice + (TakeProfitPips * UsePoint);
buyTakeProfitPrice = NormalizeDouble(buyTakeProfitPrice, Digits);
}
// 卖单止盈计算
double sellTakeProfitPrice = 0.0;
if (TakeProfitPips > 0)
{
sellTakeProfitPrice = openPrice - (TakeProfitPips * UsePoint);
sellTakeProfitPrice = NormalizeDouble(sellTakeProfitPrice, Digits);
}
其他止损设置方法
除了使用固定的 pips 数,还可以采用更动态的方法来设置止损或止盈,常见的有:
-
基于K线高低点: 例如,将买单止损设置在上一根 K 线 (Bar 1) 的最低价下方 2 pips。 MQL4提供了预定义的价格时间序列数组,如
Low[]
(最低价)、High[]
(最高价)。数组索引代表 K 线的偏移量:Low[0]
是当前 K 线 (Bar 0, 正在形成中) 的最低价,Low[1]
是上一根已完成 K 线 (Bar 1) 的最低价,以此类推。// 获取上一根 K 线的最低价 double prevBarLow = Low[1]; // 计算 2 pips 的价格距离 double offset = 2 * UsePoint; // 计算止损价 double buyStopLossPrice = NormalizeDouble(prevBarLow - offset, Digits);
-
基于过去N 根K线的极值: 例如,将买单止损设置在过去 10 根 K 线(包括当前 K 线)的最低最低价。可以使用 MQL4内置函数
iLowest()
(查找最低值) 或iHighest()
(查找最高值)。iLowest(symbol, timeframe, type, count, start)
返回指定范围内具有最低值的 K 线相对于当前 K 线 (Bar 0) 的索引 (index or shift)。示例:买单止损设在过去 10 根 K 线的最低价下方一定距离
int lookbackPeriod = 10; // 回看周期 int startBar = 0; // 从当前 K 线开始 (索引 0) // 查找最低价(MODE_LOW)在过去10根K线(从Bar 0 到 Bar 9)中的 K 线索引 int lowestBarIndex = iLowest(NULL, 0, MODE_LOW, lookbackPeriod, startBar); // 获取该 K 线的最低价 double lowestLowPrice = Low[lowestBarIndex]; // 设置止损价 (例如,在最低价下方再加一个缓冲 pips) double bufferPips = 1; double buyStopLossPrice = NormalizeDouble(lowestLowPrice - (bufferPips * UsePoint), Digits);
iHighest()
函数用法类似,配合MODE_HIGH
来查找区间的最高价,常用于设置卖单的止损。 -
基于技术指标值: 例如,将止损设置在某条移动平均线 (MA) 的当前值下方。
// 假设已用 iMA() 计算出 MA 值并存入 maValue double maValue = iMA(NULL, 0, 20, 0, MODE_SMA, PRICE_CLOSE, 0); // 示例: 20周期SMA // 设置买单止损在 MA 值下方一定距离 (例如 3 pips) double bufferPips = 3; double buyStopLossPrice = NormalizeDouble(maValue - (bufferPips * UsePoint), Digits);
这些只是设定止损止盈的一些常用思路。您可以结合您的交易策略、风险偏好以及对技术分析的理解,设计出更多样化的动态止损止盈机制。
获取订单信息
当一个订单成功下达后(无论是市价单还是挂单),在后续的程序运行中,我们常常需要获取这个订单的详细信息,以便进行状态跟踪、修改(如调整 SL/TP)或最终平仓/删除。这个“获取信息”的过程,第一步是选中目标订单,这需要用到 OrderSelect()
函数。
OrderSelect()
函数允许我们通过两种方式定位并选中订单:一是使用订单独一无二的订单号;二是通过遍历当前订单池(所有持仓订单和挂单的集合),按其在池中的位置索引 来逐个选中。
一旦通过 OrderSelect()
成功选中了一个订单,MQL4就会将该订单的全部信息加载到一块临时内存区域。之后,我们就可以调用一系列以 Order
开头的特定函数(如 OrderType()
, OrderOpenPrice()
, OrderStopLoss()
等)来读取这块内存区域中的信息,从而获得关于当前选中订单的各种属性。
OrderSelect() 函数详解
OrderSelect()
函数是用于访问和操作现有订单(包括持仓订单和挂单)的核心入口。它的主要作用是在当前的订单池中选中一个特定的订单,以便后续可以通过一系列 Order*()
函数来读取该订单的详细信息,或者调用 OrderClose()
, OrderDelete()
, OrderModify()
等函数来对其进行平仓、删除或修改操作。
语法:
bool OrderSelect(int index, int select_by, int pool=MODE_TRADES);
index
(整数): 用于定位订单的标识符。其具体含义由select_by
参数决定。select_by
(整数): 指定index
参数的类型:SELECT_BY_TICKET
: 表示index
是一个订单号 (Ticket Number)。这是最常用的方式,因为订单号是唯一的。SELECT_BY_POS
: 表示index
是订单在pool
指定的订单池中的位置索引,从 0 开始计数。这种方式通常用于循环遍历订单池中的所有订单(例如,使用for
循环配合OrdersTotal()
)。
pool
(整数, 可选): 指定要搜索哪个订单池:MODE_TRADES
(默认值): 在当前持仓订单和未成交挂单池中搜索。MODE_HISTORY
: 在已平仓或已取消的订单历史记录池中搜索。
返回值 (bool
类型):
true
: 函数成功找到了index
指定的订单并将其选中。此时,MQL4内部环境已准备好该订单的信息供Order*()
函数读取。false
: 未能找到或选中订单(例如,订单号无效,或索引超出范围)。如果返回false
,调用GetLastError()
可以获取失败原因。
示例 (按订单号选择):
假设变量 myOrderTicket
存储了一个您想查询的订单号。
if (OrderSelect(myOrderTicket, SELECT_BY_TICKET, MODE_TRADES))
{
// 订单成功选中!现在可以安全地调用 Order*() 函数获取它的信息了
Print("订单 #", myOrderTicket, " 类型: ", OrderType(), ", 手数: ", OrderLots());
}
else
{
Print("无法选中订单 #", myOrderTicket, ", 错误代码: ", GetLastError());
}
核心要点: 必须先成功调用 OrderSelect()
(即其返回值为 true
),然后才能使用下面的 Order*()
函数来获取当前选中订单的信息。
常用的订单信息函数 (在 OrderSelect() 成功后调用): (完整列表请查阅参考文档 -> 交易函数)
OrderSymbol()
: 返回订单的交易品种名称 (string)。OrderType()
: 返回订单类型 (int, 对应OP_BUY
,OP_SELL
,OP_BUYSTOP
等常量)。OrderOpenPrice()
: 返回订单的开仓价格 (double)。OrderLots()
: 返回订单的交易手数 (double)。OrderStopLoss()
: 返回订单设置的止损价格 (double, 未设置则为 0)。OrderTakeProfit()
: 返回订单设置的止盈价格 (double, 未设置则为 0)。OrderTicket()
: 返回当前选中订单的订单号 (int)。(在使用SELECT_BY_POS
遍历时特别有用)。OrderMagicNumber()
: 返回订单的魔术数字 (int)。(EA 管理自身订单的关键)。OrderComment()
: 返回订单的注释 (string)。OrderClosePrice()
: 返回已平仓订单的平仓价格 (double, 必须在MODE_HISTORY
池中选择)。OrderOpenTime()
: 返回订单的开仓时间 (datetime
)。OrderCloseTime()
: 返回订单的关闭/取消时间 (datetime
, 对于未关闭的订单返回 0)。OrderProfit()
: 返回订单当前的浮动盈亏或已实现盈亏 (double, 以账户基础货币计)。OrderSwap()
: 返回订单的隔夜利息 (double)。OrderCommission()
: 返回订单的手续费 (double)。
在执行平仓或修改订单的操作之前,通常都需要先调用 OrderSelect()
选中目标订单,并可能需要调用上述函数获取一些必要信息(如手数、类型等)以进行判断或作为后续操作的参数。