MQL5 中的交易操作 - 很简单(基础篇)
◍ 用 MQL5 把买卖动作自动化
做算法交易,绕不开三个硬问题:交易什么、何时交易、如何用代码下单。前两个属于策略层,第三个才是 MQL5 真正要接住的活——把明确的买卖意图翻译成平台能执行的指令。 在 MetaTrader 5 环境里,交易操作本身并不玄学。官方示例文档显示,一篇基础讲解帖在 2013 年 10 月 24 日发布后,累计获得 13474 次查看与 39 条讨论,说明手动转自动的需求一直很实。 本文聚焦第三问:怎样用 MQL5 语言完成下单、平仓这类基础动作。品种选择和入场规则先放一边,先把‘怎么让机器人替你点鼠标’跑通,后面接策略才有着力点。外汇与贵金属杠杆高,自动化放大效率的同时也放大风险,任何下单逻辑都先在策略测试器里验证。
「MQL5 怎么把下单变成几行代码」
做算法交易,最耗时的从来不是想逻辑,而是把想法接进券商通道。MQL5 把订单、持仓、交易请求都封装成现成函数,开发者省掉大量底层通信和回报解析的活。 核心就两个发送函数:OrderSend() 是同步发单,代码会卡住等服务器回执;OrderSendAsync() 是异步发单,丢出去就继续跑,回执靠事件回调拿。实战里高频或挂多品种时,异步更不容易卡主线程。 除了发单,MQL5 还能拉交易品种的成交一览表、翻交易历史、捕获成交事件,也能画自定义指标和界面。外汇和贵金属杠杆高、滑点跳空频繁,异步发单配合事件处理,能降低错单概率,但断网或流动性枯竭时仍可能失败,需自行做超时与重检。
下单动作其实就这几类
在 MT5 的 EA 里,真正绕不开的交易动作只有四组:市价直接 BUY/SELL、按条件挂单、改单或撤单、以及平仓/加仓/减仓/反手。它们全部走 OrderSend() 或它的异步版 OrderSendAsync(),底层靠 MqlTradeRequest 结构把请求描述清楚。 挂单类型严格对应 ENUM_ORDER_TYPE 枚举:BUY STOP 与 SELL STOP 是突破低于市价的指定位才触发;BUY LIMIT 与 SELL LIMIT 则是回到高于市价的指定位才成交;BUY STOP LIMIT / SELL STOP LIMIT 先触价再转成对应的 LIMIT 挂单。外汇与贵金属杠杆高,挂单滑点和拒单概率都不容忽视,回测之外必须在实盘点差环境验证。 改挂单、删挂单、调持仓,用的还是同一套 OrderSend 逻辑,难点只在把 MqlTradeRequest 字段填对和处理返回结果。开 MT5 按 F4 进 MetaEditor,新建 EA 把请求结构打印出来,比看文档直观得多。
◍ 用 CAccountInfo 给 EA 加一道实盘防火墙
写训练型 EA 时,第一道关卡应该是确认它跑在什么账户上。MT5 标准库里的 CAccountInfo 类能直接拉取账号、账户模式、交易权限等关键字段,比手动调 AccountInfoInteger 更省事。 下面这段初始化逻辑,会在 OnInit 里先取 login,再判断 TradeMode。一旦识别到 ACCOUNT_TRADE_MODE_REAL,立刻弹窗并 return(-1) 终止 EA——这对防止训练代码误上实盘很有用,外汇和贵金属实盘波动剧烈,误触发下单可能造成真实亏损。 除实盘拦截外,代码还顺手打印了 TradeAllowed、TradeExpert、LimitOrders 以及 Balance/Profit/Equity。把这段塞进你的 EA,跑起来后翻日志就能快速确认环境是否合规,不用等到报错才发现问题。 别把正态当圣经:账户若用只读的投资人密码登录,TradeAllowed 会返回 false,这时候 EA 不会下单但也不会崩,日志里那句‘Investor password’才是真信号。
class="macro">#include <Trade\AccountInfo.mqh> class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| EA交易初始化函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//--- 处理帐户的对象 CAccountInfo account; class=class="str">"cmt">//--- 接收EA交易系统所运行帐户的账号 class="type">long login=account.Login(); Print("Login=",login); class=class="str">"cmt">//--- 声明帐户类型 ENUM_ACCOUNT_TRADE_MODE account_type=account.TradeMode(); class=class="str">"cmt">//--- 如果是实盘账户,立即停止EA交易! if(account_type==ACCOUNT_TRADE_MODE_REAL) { MessageBox("Trading on a real account is forbidden, disabling","The Expert Advisor has been launched on a real account!"); class="kw">return(-class="num">1); } class=class="str">"cmt">//--- 显示帐户类型 Print("Account type: ",EnumToString(account_type)); class=class="str">"cmt">//--- 声明我们可以在此帐户上进行交易 if(account.TradeAllowed()) Print("Trading on this account is allowed"); else Print("Trading on this account is forbidden: you may have entered class="kw">using the Investor password"); class=class="str">"cmt">//--- 声明我们可以在此帐户上用EA交易 if(account.TradeExpert()) Print("Automated trading on this account is allowed"); else Print("Automated trading class="kw">using Expert Advisors and scripts on this account is forbidden"); class=class="str">"cmt">//--- 声明已经设置了允许的订单数量 class="type">int orders_limit=account.LimitOrders(); if(orders_limit!=class="num">0)Print("Maximum permissible amount of active pending orders: ",orders_limit); class=class="str">"cmt">//--- 显示公司和服务器名称 Print(account.Company(),": server ",account.Server()); class=class="str">"cmt">//--- 最后显示帐户的结余和当前利润 Print("Balance=",account.Balance()," Profit=",account.Profit()," Equity=",account.Equity()); Print(__FUNCTION__," completed"); class=class="str">"cmt">//--- class="kw">return(class="num">0); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| EA交易去初始化函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnDeinit(const class="type">int reason) { class=class="str">"cmt">//--- } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| EAtick函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnTick() { class=class="str">"cmt">//--- }
「用 CSymbolInfo 把品种参数一次性读出来」
在 MT5 里下单前,最容易被忽略的一步是确认当前品种的交易约束。CSymbolInfo 这个类把合约大小、点差、止损冻结水平、最小/最大手数等属性封装成方法,初始化时调一次就能拿到全部关键设置。 下面这段 OnInit 里的代码在 EURUSD 上跑通后,终端会打印出类似 Bid=1.08542 Ask=1.08544、Spread=2 pips、LotsMin=0.01 LotsMax=500 的数据,具体数值随经纪商和行情实时变化。 别把正态当圣经 这些属性不是写死的。SpreadFloat 返回 true 说明点差是浮动的,亚盘和重大数据发布时 Spread 可能从 2 跳到 20 以上,挂单距离必须动态读取 StopsLevel 和 FreezeLevel,硬凑固定点数容易直接报『无效止损』。 外汇和贵金属杠杆高、波动剧烈,参数读错可能导致订单被拒或异常平仓。把这段代码贴进 EA 初始化,你就能在日志里直观核对品种规则,再决定后续开仓逻辑怎么写。
class="macro">#include<Trade\SymbolInfo.mqh> class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| EA初始化函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//--- 接收交易品种设置参数的对象 CSymbolInfo symbol_info; class=class="str">"cmt">//--- 为合适的交易品种设置名称 symbol_info.Name(_Symbol); class=class="str">"cmt">//--- 接受当前价格相关信息并显示 symbol_info.RefreshRates(); Print(symbol_info.Name()," (",symbol_info.Description(),")", " Bid=",symbol_info.Bid()," Ask=",symbol_info.Ask()); class=class="str">"cmt">//--- 接收交易操作的最小冻结水平 Print("StopsLevel=",symbol_info.StopsLevel()," pips, FreezeLevel=", symbol_info.FreezeLevel()," pips"); class=class="str">"cmt">//--- 接收小数位数和点值 Print("Digits=",symbol_info.Digits(), ", Point=",DoubleToString(symbol_info.Point(),symbol_info.Digits())); class=class="str">"cmt">//--- 点差信息 Print("SpreadFloat=",symbol_info.SpreadFloat(),", Spread(current)=", symbol_info.Spread()," pips"); class=class="str">"cmt">//--- 请求限价订单的执行类型 Print("Limitations for trade operations: ",EnumToString(symbol_info.TradeMode()), " (",symbol_info.TradeModeDescription(),")"); class=class="str">"cmt">//--- 声明交易执行模式 Print("Trades execution mode: ",EnumToString(symbol_info.TradeExecution()), " (",symbol_info.TradeExecutionDescription(),")"); class=class="str">"cmt">//--- 声明合约价格计算方法 Print("Contract price calculation: ",EnumToString(symbol_info.TradeCalcMode()), " (",symbol_info.TradeCalcModeDescription(),")"); class=class="str">"cmt">//--- 合约大小 Print("Standard contract size: ",symbol_info.ContractSize(), " (",symbol_info.CurrencyBase(),")"); class=class="str">"cmt">//--- 交易操作的最小和最大交易量 Print("Volume info: LotsMin=",symbol_info.LotsMin()," LotsMax=",symbol_info.LotsMax(), " LotsStep=",symbol_info.LotsStep()); class=class="str">"cmt">//--- Print(__FUNCTION__," completed"); class=class="str">"cmt">//--- class="kw">return(class="num">0); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| EA交易去初始化函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnDeinit(const class="type">int reason) { class=class="str">"cmt">//--- } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| EA的tick函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnTick() { class=class="str">"cmt">//--- }