如何向程序员定制 EA:需求、协作与防坑指南
从想要个能赚钱的机器人到可验收的算法说明书——交易者与技术之间的正确打开方式。
一、EA 下单的两种 API
MQL5 下单有两条路:旧式的 OrderSend() 直接发交易请求,参数多、需自己拼 MqlTradeRequest;新式的 CTrade 类(标准库 Trade.mqh)把常用操作封装成 Buy()/Sell()/BuyLimit() 等方法,内部处理好请求结构。新手强烈建议用 CTrade,少踩一半坑。
无论哪条路,下单本质都是「构造请求 → 发给交易服务器 → 检查返回结果」。理解这个流程,比背函数签名更重要:你发的不是指令,而是一份可能被拒绝的申请书。
二、下单前的准备:句柄与品种检查
下单前务必确认品种已选中和可交易:SymbolSelect(_Symbol,true) 把品种加入市场报价窗口,SymbolInfoInteger(_Symbol,SYMBOL_TRADE_MODE) 检查是否允许交易。跳过这步,EA 在冷门品种上一下单就报「品种未选中」或「禁止交易」。
if(!SymbolSelect(_Symbol, true)) { Print("symbol select failed"); return; }
long tradeMode = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_MODE);
if(tradeMode == SYMBOL_TRADE_MODE_DISABLED) { Print("trading disabled"); return; }
三、市价单 Market
CTrade::Buy(lots, _Symbol, price, sl, tp, comment) 发买市价单;price 传 0 表示用当前最优价。注意市价单的成交价由服务器决定,可能和你看到的略有偏差(滑点)。在跳空行情里,这个偏差会被放大,所以重仓时尤其要控制单笔风险。
CTrade trade;
trade.Buy(0.1, _Symbol, 0, 0, 0, "test buy");
if(trade.ResultRetcode() != TRADE_RETCODE_DONE)
Print("buy failed: ", trade.ResultRetcodeDescription());
四、挂单 Limit / Stop
限价单在更优价格成交,适合「等回踩再进」;止损单在突破价格成交,适合「突破跟随」。两者都用 CTrade 的 BuyLimit/SellStop 等方法,价格必须对齐 TickSize 网格(见价格离散性一文),否则会被拒。
挂单还有一个常被忽略的细节:有效期。MT5 的挂单默认在达到条件前一直有效(除非设了到期),你要清楚它会不会在不利的时点自动触发,尤其是在你不在电脑前的时候。
五、止损与止盈怎么挂
市价单可以在下单时一并带上 sl/tp;已持仓则要调用 PositionModify 或用 CTrade 修改。止损价同样要对齐网格,且不能在市价「太近」——很多券商对止损距市价有最小距离限制(StopLevel),太近会被拒。
double sl = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID) - 200*_Point, _digits); trade.Buy(0.1, _Symbol, 0, sl, 0, "with SL");
六、错误处理:永远看 Retcode
下单后第一件事是检查返回码 trade.ResultRetcode()。常见拒绝如 TRADE_RETCODE_INVALID_PRICE(价格非法)、_INVALID_VOLUME(手数非法)、_NO_MONEY(保证金不足)、_MARKET_CLOSED(市场休市)。不检查就假设成功,是 EA 半夜偷偷不交易却没人知道的根源。
七、常见坑清单
- 价格/手数没对齐 TickSize / VOLUME_STEP 被拒
- 止损距市价小于 StopLevel 被拒
- 用 price[0](未收盘)算止损导致未来函数
- 下单前没 SymbolSelect 导致品种未选中
- 不检查 Retcode,失败静默发生
八、实战下单模板
把上面几点收敛成一个可复用的下单函数:先检查品种与手数合法性,再算对齐后的 sl/tp,调 CTrade,最后根据 Retcode 记录成功或失败原因。这样一个函数用在所有信号上,既少错又易排查。
bool SafeBuy(double lots, double sl, double tp)
{
if(lots < SymbolInfoInteger(_Symbol, SYMBOL_VOLUME_MIN)) return false;
double p = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
trade.Buy(lots, _Symbol, p, sl, tp, "SafeBuy");
if(trade.ResultRetcode() != TRADE_RETCODE_DONE)
{ Print("SafeBuy fail: ", trade.ResultRetcodeDescription()); return false; }
return true;
}
九、把下单封装成可复用库
实战项目里,把 SafeBuy/SafeSell 这类带校验与日志的下单函数抽成独立的 .mqh 函数库,让所有 EA 共用。好处是风控逻辑只有一份、改一处全生效,且每次下单都留下统一格式的记录,便于事后审计「那天空单到底为什么没成」。这也是团队协作时避免各 EA 各写一套、互相矛盾的关键。