到目前为止,我们的EA已经学会了如何用一个固定的点数去下单。但这还远远不够。一个专业的EA,应该能适应不同的市场环境,能根据市场的结构来动态调整自己的行为。
要实现这一点,我们的EA首先需要拥有一双能观察市场的“眼睛”,以及能审视自身的“意识”。今天,我们就来为它安装这些高级插件。
Part 1:为EA安装“眼睛”—— MarketInfo()
函数
要做出智能决策,首先得有信息。MarketInfo()
函数就是EA获取市场实时信息的“万能传感器”。它可以“感知”到当前的点差、最小止损距离,甚至能“看到”其他货币对的价格。
函数语法: double MarketInfo(string symbol, int type);
symbol
: 你想查询哪个品种?用Symbol()
代表当前图表品种,或直接写"EURUSD"
。type
: 你想查询什么信息?这是一个整数常量,代表不同的信息类型。
EA最常用的几个“传感器”类型(type):
MODE_SPREAD
: 感知当前实时点差。注意,单位是Point。MODE_STOPLEVEL
: 感知经纪商的规则——你的挂单或止损距离市价,最少要有多少Points。下单前检查这个值,是避免“无效止损”错误的根本。MODE_DIGITS
: 感知报价精度,返回小数位数(比如5或4)。MODE_POINT
: 感知最小价格单位(比如0.00001)。MODE_BID
/MODE_ASK
: 感知指定品种的实时买卖价。这让跨品种套利策略成为可能。MODE_HIGH
/MODE_LOW
: 感知当前这根K线的最高/最低价。
Part 2:策略进化——从“固定止损”到“动态止损”
拥有了MarketInfo()
这双“眼睛”后,我们就可以让止损/止盈的设置变得更加智能,不再是写死的固定点数。
方法一:按固定Pips计算(复习与巩固)
这是我们之前学过的基础,核心是使用我们自创的PipPoint()
“通用翻译器”,确保pips计算在不同平台的一致性。
- 买单止损价 =
Ask - (止损Pips * PipPoint())
- 卖单止损价 =
Bid + (止损Pips * PipPoint())
- 止盈则方向相反。
// --- 买单止损计算 ---
double openPrice_Buy = Ask;
double buyStopLossPrice = 0.0;
if (StopLossPips > 0)
{
// 核心:使用我们标准化的PipPoint()函数返回值
buyStopLossPrice = openPrice_Buy - (StopLossPips * g_onePipValue); // g_onePipValue 是PipPoint()的计算结果
buyStopLossPrice = NormalizeDouble(buyStopLossPrice, Digits);
}
// --- 卖单止损计算 ---
double openPrice_Sell = Bid;
double sellStopLossPrice = 0.0;
if (StopLossPips > 0)
{
sellStopLossPrice = openPrice_Sell + (StopLossPips * g_onePipValue);
sellStopLossPrice = NormalizeDouble(sellStopLossPrice, Digits);
}
方法二:基于K线高低点——让市场结构告诉你止损在哪
这是真正的价格行为交易思路。一个好的止损,应该放在市场的关键结构位,而不是一个随意数字。
// 示例:将买单止损设置在【上一根K线】的最低价下方2 pips
// Low[] 是一个预定义的数组,Low[1]就代表上根K线的最低价
double prevBarLow = Low[1];
double offset = 2 * g_onePipValue; // 计算2 pips的缓冲距离
double buyStopLossPrice = NormalizeDouble(prevBarLow - offset, Digits);
方法三:基于N根K线极值——寻找更强的支撑/阻力
我们可以用iLowest()
和iHighest()
函数,来寻找过去一段时间内的最低/最高点作为我们止损的依据。
// 示例:将买单止损设置在过去10根K线的最低点下方1 pip
int lookbackPeriod = 10; // 回看10根K线
// 找到过去10根K线中,最低价出现的那根K线的“索引号”
int lowestBarIndex = iLowest(NULL, 0, MODE_LOW, lookbackPeriod, 0);
// 根据索引号,从Low[]数组中获取那个最低价
double lowestLowPrice = Low[lowestBarIndex];
double bufferPips = 1; // 缓冲1 pip
double buyStopLossPrice = NormalizeDouble(lowestLowPrice - (bufferPips * g_onePipValue), Digits);
方法四:基于技术指标——动态的“移动防线”
让止损跟随指标移动,是很多趋势策略的核心。比如,用移动平均线(MA)作为止损。
// 示例:将买单止损设置在20周期MA值下方3 pips处
double maValue = iMA(NULL, 0, 20, 0, MODE_SMA, PRICE_CLOSE, 0); // 获取当前MA值
double bufferPips = 3;
double buyStopLossPrice = NormalizeDouble(maValue - (bufferPips * g_onePipValue), Digits);
Part 3:为EA安装“自我意识”—— OrderSelect()
函数
交易执行后,EA不能当“甩手掌柜”。它必须能随时“检阅”自己的持仓,进行管理。OrderSelect()
就是EA与持仓订单沟通的“对讲机”。
核心工作流程:先用OrderSelect()
选中一个订单,然后才能用其他Order*()
函数读取它的信息。
OrderSelect()
函数详解: bool OrderSelect(int index, int select_by, int pool=MODE_TRADES);
index
: 你要找的订单标识。select_by
: 你用什么方式去找?SELECT_BY_TICKET
: 用独一无二的订单号去找。(最常用)SELECT_BY_POS
: 按订单在列表中的位置去找。(用于遍历所有订单)
pool
: 在哪个列表里找?MODE_TRADES
: 在当前持仓和挂单列表里找。(默认)MODE_HISTORY
: 在历史订单列表里找。
返回值:true
表示找到了,false
表示没找到。
学长避坑指南:所有Order*()
信息读取函数,都必须、必须、必须放在if(OrderSelect(...)
== true)
的大括号{}
内部! 如果在OrderSelect()
失败后,你还去调用OrderOpenPrice()
,你得到的将是上一个被选中订单的“幽灵”信息,这会引发灾难性的逻辑错误。
读取订单信息
一旦OrderSelect()
成功,你就可以像查户口一样,用下面这些函数获取订单的一切信息:
OrderType()
: 订单类型 (买/卖/挂单?)OrderLots()
: 手数OrderOpenPrice()
: 开仓价OrderStopLoss()
/OrderTakeProfit()
: 止损/止盈价OrderMagicNumber()
: 魔术号 (EA识别自己订单的关键)OrderProfit()
: 当前浮动盈亏OrderSwap()
: 隔夜利息OrderComment()
: 订单注释- …等等
示例:按订单号查询信息
int myOrderTicket = 12345678; // 假设这是我们想查询的订单号
if (OrderSelect(myOrderTicket, SELECT_BY_TICKET, MODE_TRADES))
{
// 安全区:只有在这里,下面的信息才是准确的
Print("订单 #", myOrderTicket, " 查询成功!");
Print("品种: ", OrderSymbol());
Print("类型: ", OrderType());
Print("手数: ", OrderLots());
Print("开仓价: ", OrderOpenPrice());
Print("当前浮动盈利: ", OrderProfit());
}
else
{
Print("无法选中订单 #", myOrderTicket, ",错误代码: ", GetLastError());
}
掌握了MarketInfo()
和OrderSelect()
,你的EA就从一个只会“下单”的“执行者”,进化成了一个能“观察环境”、能“自我审视”的“决策者”。这是通往高级、智能化EA的必经之路。