MQL5 标准交易类库入门:用内置类给 EA 开发省掉重复造轮子(基础篇)
📚

MQL5 标准交易类库入门:用内置类给 EA 开发省掉重复造轮子(基础篇)

(1/3)· 从零认识 Trade 类体系,把账户、订单、持仓的底层访问封装成几行调用

含代码示例实战向 第 1/3 篇
很多交易者拿到 MQL5 想写 EA,第一反应是手写订单发送和账户查询逻辑,结果在报错和重连里耗掉大半时间。标准类库早把这套繁琐流程封装好了,只是没人告诉你从哪个对象先 new 起。先理解类能帮你做什么,比死磕源码实现更划算。

◍ 用标准库写 EA 省在哪

在 MT5 里手搓下单逻辑,光是持仓选择、市价单与挂单的差异就要写一堆容错,新手很容易在品种点值换算上翻车。MQL5 自带的标准交易类库(Trade 和 PositionInfo 等)把这些封装好了,直接调方法就能发单、改仓、查盈亏。 实际跑过的人知道,用 CTrade 类下一单黄金 XAUUSD,0.1 手市价买,核心就三行,比自己拼 MQL5 原生 OrderSend 请求结构少写二十行以上。外汇和贵金属杠杆高,滑点和点值波动大,封装层至少帮你挡掉部分参数拼错导致的异常下单。 类库不是银弹,它默认不替你做资金管理,也不管信号逻辑。你仍要在 OnTick 里自己判断入场,再调用类库方法执行。开 MT5 新建 EA 时勾选「使用标准库」,就能直接 #include <Trade/Trade.mqh> 验证这套路径。

「标准类库藏在 Include 目录里」

MT5 客户端装好后,MQL5 标准类库就静默躺在 MQL5\Include\ 目录下,不打开文件管理器基本不会注意到。里面按功能拆成 Arrays、ChartObjects、Charts、Files、Indicators、Strings、Trade 七个子分类,EA、指标、脚本都能直接 include 调用。 做自动交易最常用的是 Trade 类,它把下单、平仓、改仓这些动作封装成方法,省掉手写交易请求结构的麻烦。本文后续就只围绕内置交易类讲 EA 怎么写,策略逻辑很简单:条件触发后,对已有仓位做平仓或修改。 如果你已经懂类的继承和实例化,直接往下看就行。零基础的话,先去翻官方分步指南和面向对象编程那两篇,不然 Trade 类的成员函数会看得一头雾水。外汇和贵金属杠杆高,跑任何 EA 前都先在策略测试器用历史数据验证。

用标准类接管账户与订单信息读取

MT5 标准库把账户、品种、历史订单、挂单、成交和持仓分别封装成六个类,开发者不必重写底层调用,直接实例化就能拉数据。对外汇与贵金属交易者而言,这意味着 EA 里读取杠杆、点差、保证金和持仓状态可以压缩成几行代码,但外汇贵金属杠杆品种波动剧烈,实盘前务必在策略测试器里验证边界条件。 以 CAccountInfo 为例,先声明对象再调成员函数: CAccountInfo myaccount; double lev = myaccount.Leverage(); bool can_trade = myaccount.TradeAllowed(); 上面两行分别取当前账户杠杆和交易开关,若 TradeAllowed() 返回 false,EA 应直接跳过下单逻辑。MarginCheck() 接收品种、订单类型、手数和价格四个参数,返回开仓所需预付款,是手数风控的核心调用。 CSymbolInfo 解决品种属性读取。创建 mysymbol 后,用 Name() 设定品种,RefreshRates() 拉最新报价,返回 bool 告诉你成功与否;Spread() 给当前点差点数,StopsLevel() 返回止损最低距离点数,挂单或修改止损前不查它就可能报『离市价太近』错误。Digits() 和 Point() 决定价格规范化位数,黄金 XAUUSD 通常 Digits()=2,Point()=0.01。 历史与实时订单分在两个类:CHistoryOrderInfo 用 Ticket(ulong) 选订单号后读 TimeSetup()、State()、PriceOpen();COrderInfo 管挂单,多了 StopLoss()、TakeProfit() 和 StoreState()/CheckState() 这对,能缓存挂单快照再比对改动。CDealInfo 走成交记录,Entry() 区分 DEAL_ENTRY_IN/OUT;CPositionInfo 用 Select(品种) 后取 Time()、Type() 遍历敞口仓。 这套类不暴露内部实现,你只管传参取数。开 MT5 按 F4 建个空白 EA,把 myaccount、mysymbol 对象敲进去,Print 出 Leverage() 和 Spread(),比读文档更快建立手感。

◍ 用 CTrade 把下单逻辑跑通

CTrade 类把 MQL5 里零散的交易函数收拢成一个对象,省去自己拼交易请求的麻烦。先声明 CTrade mytrade;,再设幻数和允许偏离点数,后面所有下单、改单、平仓都走这个对象的方法。 mytrade.Buy(volume, symbol, price, sl, tp) 用当前 Ask 价建多仓,Sell() 对应 Bid 价建空仓;挂单侧有 BuyStop / SellStop / BuyLimit / SellLimit,默认 time 为 ORDER_TIME_GTC、expiration 为 0,和你心意一致时可不填。外汇与贵金属杠杆高,实盘前务必在策略测试器用极小手数验证滑点与离场逻辑。 出错时不要瞎猜。mytrade.ResultRetcode() 给返回码,mytrade.RequestSL() 这类请求函数能把发往服务器的参数打出来。比如收到 Invalid Stops,调 RequestSL() 一看就知道止损价算错在哪。 下方代码演示了账户对象取登录号、交易模式描述与杠杆,并判断账户是否允许 EA 交易——这些在用 CTrade 发单前就该先确认。

MQL5 / C++
class=class="str">"cmt">//--- The AccountInfo Class Object
CAccountInfo myaccount;
class=class="str">"cmt">// returns account number, example class="num">7770
class="type">long accountno = myaccount.Login()
class=class="str">"cmt">// returns Demo trading account,
class=class="str">"cmt">// or Real trading account or Contest trading account
class="type">class="kw">string  acc_trading_mode = myaccount.TradeModeDescription();
class=class="str">"cmt">// returns leverage given to the active account
class="type">long acc_leverage = myaccount.Leverage(); 

if (myaccount.TradeAllowed())
{
    class=class="str">"cmt">// trade is allowed
}
else
{
  class=class="str">"cmt">// trade is not allowed
}
if (myaccount.TradeExpert())
{
   class=class="str">"cmt">// Expert Advisor trade is allowed
}
else
{
   class=class="str">"cmt">// Expert Advisor trade is not allowed
}
class=class="str">"cmt">// returns account balance in the deposit currency
class="type">class="kw">double acс_balance =  myaccount.Balance();
class=class="str">"cmt">// returns account profit in deposit currency

「账户与品种对象的字段抓取实战」

在 MT5 里做风控前,先把账户和品种的真实状态读出来。下面这段用 CAccountInfo、CSymbolInfo、CHistoryOrderInfo 三个标准类,把浮动盈利、空闲保证金、点差、合约大小等字段一次性拉到本地变量,开 MT5 新建 EA 粘贴就能跑。 double acc_profit = myaccount.Profit(); 取当前账户浮动盈利;myaccount.FreeMargin() 返回可用保证金;myaccount.Currency() 拿到账户结算币种,比如 "USD"。用 myaccount.OrderProfitCheck(_Symbol, ORDER_TYPE_BUY, 1.0, 1.2950, 1.3235) 可以纯计算不下单地预估:EURUSD 在 1.2950 买、1.3235 卖、1 标准手的可能盈利,Print 出来即可核对。 CSymbolInfo 对象必须先 Name(_Symbol) 再 Refresh(),否则 Bid()/Ask() 可能读到旧值。代码里用 mysymbol.VolumeHigh() 取当日最高成交量、mysymbol.Spread() 取点差整数点数、mysymbol.StopsLevel() 取止损最小距离、mysymbol.FreezeLevel() 取冻结点数——这些在贵金属跳空时可能瞬间扩大,外汇和贵金属均属高风险品种,数值仅供参考。 品种选择状态也值得留意:mysymbol.Select(true) 把品种加入市场报价窗口,Select(false) 移除;TradeMode()==SYMBOL_TRADE_MODE_FULL 才代表无交易限制。历史订单用 HistorySelect(0, TimeCurrent()) 拉全量,HistoryOrdersTotal() 给总数,再按 ticket 循环可取每笔明细,用于复盘胜率。

MQL5 / C++
class="type">class="kw">double acc_profit = myaccount.Profit();
class=class="str">"cmt">// returns free margin for active account
class="type">class="kw">double acc_free_margin = myaccount.FreeMargin();
class="type">class="kw">double acc_currency = myaccount.Currency();
class="type">class="kw">double op_profit=myaccount.OrderProfitCheck(_Symbol,ORDER_TYPE_BUY,class="num">1.0,class="num">1.2950,class="num">1.3235);
Print("The amount of Profit for deal buy EURUSD","at class="num">1.2950 and sell at class="num">1.3235 is: ",op_profit);
class=class="str">"cmt">// depending on the type of position to open - in our case buy
class="type">class="kw">double price=SymbolInfoDouble(_Symbol,SYMBOL_ASK);
class="type">class="kw">double margin_req=myaccount.MarginCheck(_Symbol,ORDER_TYPE_BUY,LOT,price);
class="type">class="kw">double acc_fm=myaccount.FreeMarginCheck(_Symbol,ORDER_TYPE_BUY,LOT,price);
class="type">class="kw">double max_lot=myaccount.MaxLotCheck(_Symbol,ORDER_TYPE_BUY,price);
class=class="str">"cmt">// the CSymbolInfo Class object
CSymbolInfo mysymbol;
class=class="str">"cmt">// set the symbol name for our CSymbolInfo class Object
mysymbol.Name(_Symbol);
mysymbol.Refresh();
class=class="str">"cmt">//--- Get the last price quote class="kw">using the CSymbolInfo
class=class="str">"cmt">// class object function
if (!mysymbol.RefreshRates())
{
class=class="str">"cmt">// error getting latest price quotes
}
class=class="str">"cmt">// check if symbol data are synchronized with server
if (!mysymbol.IsSynchronized())
{
class=class="str">"cmt">// error! Symbol data aren&class="macro">#x27;t synchronized with server
}
class="type">long max_vol = mysymbol.VolumeHigh();
class="type">long min_vol = mysymbol.VolumeLow();
class="type">class="kw">datetime qtime = mysymbol.Time();
class="type">int spread = mysymbol.Spread();
class="type">int stp_level = mysymbol.StopsLevel();
class="type">int frz_level = mysymbol.FreezeLevel();
class="type">class="kw">double bid = mysymbol.Bid();
class="type">class="kw">double max_bid = mysymbol.BidHigh();
class="type">class="kw">double min_bid = mysymbol.BidLow();
class="type">class="kw">double ask = mysymbol.Ask();
class="type">class="kw">double max_ask = mysymbol.AskHigh();
class="type">class="kw">double min_ask = mysymbol.AskLow();
class=class="str">"cmt">// returns "USD" for USDJPY or USDCAD
class="type">class="kw">string base_currency = mysymbol.CurrencyBase();
class="type">class="kw">double cont_size = mysymbol.ContractSize();
class="type">int s_digits = mysymbol.Digits();
class="type">class="kw">double s_point = mysymbol.Point();
class="type">class="kw">double min_lot = mysymbol.LotsMin();
class="type">class="kw">double max_lot = mysymbol.LotsMax();
class="type">class="kw">double lot_step = mysymbol.LotsStep();
class=class="str">"cmt">// A normalized current Ask price
class="type">class="kw">double n_price = mysymbol.NormalizePrice(mysymbol.Ask());
if (mysymbol.Select())
{
class=class="str">"cmt">//Symbol successfully selected
}
else
{
class=class="str">"cmt">// Symbol could not be selected
}
if (!mysymbol.Select())
{
class=class="str">"cmt">//Symbol not selected, Select the symbol
mysymbol.Select(true);
}
else
{
class=class="str">"cmt">// Symbol already selected,
class=class="str">"cmt">// remove Symbol from market watch window
mysymbol.Select(class="kw">false);
}
class="type">class="kw">double init_margin = mysymbol.MarginInitial();
if (mysymbol.TradeMode() == SYMBOL_TRADE_MODE_FULL)
{
class=class="str">"cmt">// Full trade allowed for this symbol,
class=class="str">"cmt">// no trade restrictions
}
Print("The trade mode for this symbol is", mysymbol.TradeModeDescription());
class=class="str">"cmt">// The CHistoryOrderInfo Class object
CHistoryOrderInfo myhistory;
class=class="str">"cmt">//Select all history orders within a time period
if (HistorySelect(class="num">0,TimeCurrent())) class=class="str">"cmt">// get all history orders
{
class=class="str">"cmt">// Get total orders in history
class="type">int tot_hist_orders = HistoryOrdersTotal();
class="type">class="kw">ulong h_ticket; class=class="str">"cmt">// Order ticket
for (class="type">int j=class="num">0; j<tot_hist_orders; j++)
{
 h_ticket = HistoryOrderGetTicket(j);
 if (h_ticket>class="num">0)
 {

用标准类把历史订单和持仓扒干净

MQL5 里 COrderInfoCDealInfoCPositionInfo 三个类,是把经纪商返回的原始历史与实时仓位转成可读字段的最直接路径。先给 COrderInfo 喂一个 ticket,就能脱离数组下标去取订单属性。 myhistory.Ticket(h_ticket); ulong o_ticket = myhistory.Ticket(); 上面两行把类对象绑到具体订单号,TimeSetup() 拿到挂单时间,OrderType()==ORDER_TYPE_BUY 判断是市价买,State()==ORDER_STATE_REJECTED 则说明单子被拒、根本没进场。外汇和贵金属杠杆高, rejected 状态在滑点行情里很常见,看到就得查原因而不是当没发生。 批量扫历史时,用 HistorySelect(0, TimeCurrent()) 拉全量,再用 OrdersTotal() 拿总数循环。注意 myorder.Select(OrderGetTicket(j)) 返回真才能安全读字段——TimeDone() 成交时间、Magic() 魔术码、PositionId() 关联仓位、PriceOpen() 开价、StopLoss() / TakeProfit() 防护线,PriceCurrent() 实时价也都在这。 myorder.StoreState()CheckState() 是轻量改动侦测:返回 true 表示订单状态或细节变了,适合做小布这类盯盘机器人的事件触发点。 成交明细走 CDealInfoHistoryDealGetTicket(j) 取 deal ticket,DEAL_TYPE_BUY 配合 DEAL_ENTRY_IN 能区分是开仓买还是平仓回流;Volume()Price() 就是真实成交量与成交价。 持仓侧 CPositionInfoPositionsTotal() 跑循环,Select(PositionGetSymbol(j)) 或只盯当前图 Select(_Symbol) 都行,Time() 给出仓位建立时间。把这些类接起来,你在 MT5 里就能用几十行把账户全景打印出来验证。

MQL5 / C++
myhistory.Ticket(h_ticket);
class="type">class="kw">ulong o_ticket = myhistory.Ticket();
class="type">class="kw">datetime os_time = myhistory.TimeSetup();
if (myhistory.OrderType() == ORDER_TYPE_BUY)
{
class=class="str">"cmt">// This is a buy order
}
if(myhistory.State() == ORDER_STATE_REJECTED)
{
class=class="str">"cmt">// order was rejected, not placed.
}
class="type">class="kw">datetime ot_done =  myhistory.TimeDone();
class="type">long o_magic = myhistory.Magic();
class="type">long o_posid = myhistory.PositionId();
class="type">class="kw">double o_price =  myhistory.PriceOpen();
class="type">class="kw">string o_symbol =  myhistory.Symbol();
class=class="str">"cmt">// The OrderInfo Class object
COrderInfo myorder;
class=class="str">"cmt">// Select all history orders within a time period
if (HistorySelect(class="num">0,TimeCurrent()))   class=class="str">"cmt">// get all history orders
{     
     class=class="str">"cmt">// get total orders
     class="type">int o_total = OrdersTotal();
     for (class="type">int j=class="num">0; j<o_total; j++)
     {
     if (myorder.Select(OrderGetTicket(j)))
     { class=class="str">"cmt">// order has been selected and can now be manipulated.
     }
     class="type">class="kw">ulong o_ticket = myorder.Ticket();
     class="type">class="kw">datetime o_setup = myorder.TimeSetup();
     if (myorder.Type() == ORDER_TYPE_BUY_LIMIT)
     {
     class=class="str">"cmt">// This is a Buy Limit order, etc
     }
     if (myorder.State() == ORDER_STATE_STARTED)
     {
     class=class="str">"cmt">// order has been checked
     class=class="str">"cmt">// and may soon be treated by the broker
     }
     class="type">class="kw">datetime ot_done = myorder.TimeDone();
     class="type">long o_magic =  myorder.Magic();
     class="type">long o_posid = myorder.PositionId();
     class="type">class="kw">double o_price = myorder.PriceOpen();
     class="type">class="kw">double  s_loss = myorder.StopLoss();
     class="type">class="kw">double t_profit = myorder.TakeProfit();
     class="type">class="kw">double cur_price =  myorder.PriceCurrent();
     class="type">class="kw">string o_symbol = myorder.Symbol();
     myorder.StoreState();
     if (myorder.CheckState() == true)
     {
     class=class="str">"cmt">// Our order status or details have changed
     }
     class=class="str">"cmt">// The DealInfo Class object
     CDealInfo myinfo;
     if (HistorySelect(class="num">0,TimeCurrent()))
     {   
       class=class="str">"cmt">// Get total deals in history
       class="type">int tot_deals = HistoryDealsTotal();
       class="type">class="kw">ulong d_ticket; class=class="str">"cmt">// deal ticket
       for (class="type">int j=class="num">0; j<tot_deals; j++)
       {
       d_ticket = HistoryDealGetTicket(j);
       if (d_ticket>class="num">0)
       {
       mydeal.Ticket(d_ticket);
       class="type">class="kw">ulong deal_ticket = mydeal.Ticket();
       class="type">long deal_order_no =  mydeal.Order();
       class="type">class="kw">datetime d_time = mydeal.Time();
       if (mydeal.Type() == DEAL_TYPE_BUY)
       {
       class=class="str">"cmt">// This deal was executed as a buy deal type
       }
       if (mydeal.Entry() == DEAL_ENTRY_IN)
       {
       class=class="str">"cmt">// This was an IN entry deal
       }
       class="type">long d_magic = mydeal.Magic();
       class="type">long d_post_id = mydeal.PositionId();
       class="type">class="kw">double d_price = mydeal.Price();
       class="type">class="kw">double d_vol = mydeal.Volume();
       class="type">class="kw">string d_symbol = mydeal.Symbol();
       class=class="str">"cmt">// The object of the CPositionInfo class
       CPositionInfo myposition;
       class="type">int pos_total = PositionsTotal();
       for (class="type">int j=class="num">0; j<pos_total; j++)
       {
       if (myposition.Select(PositionGetSymbol(j)))
       {
       class=class="str">"cmt">// symbol successfully selected, we can now work
       class=class="str">"cmt">// on the current open position for this symbol 
       }
       }
       if (myposition.Select(_Symbol))
       {
       class=class="str">"cmt">// symbol successfully selected, we can now work
       class=class="str">"cmt">// on the current open position for this symbol 
       }
       class="type">class="kw">datetime pos_time = myposition.Time();
交给小布盯盘看盘口
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到账户与持仓的结构化视图,你写 EA 时也能照着同样字段对齐逻辑,把重复劳动交给小布,你专注决策。

常见问题

类库位于客户端 MQL5 文件夹下的 \Include\ 子目录,按 Arrays、Trade、ChartObjects 等分类存放,EA 里用 #include 指向对应头文件即可调用。
通过对象可访问登录号、余额、杠杆、保证金等属性,具体用成员函数如 Login()、Balance() 获取,无需自己解析交易环境。
可以,本文侧重用法而非内部机制,先照示例建对象调方法,跑通后再补类概念;外汇贵金属杠杆高,回测和实盘前都要小资金验证。
能,小布盯盘的品种页直接展示账户与持仓实时结构,你可与 EA 里 CAccountInfo 的返回值交叉比对,排查字段错位类 bug。
基础篇定位是铺垫概念与使用路径,内部机制留待进阶篇;关于完整类继承与重写讨论见《编写 EA 时 MQL5 标准交易类库的使用·进阶篇》。