编写
🛠️

编写"EA 交易"时,MQL5 标准交易类库的使用·进阶篇

(2/3)· 跳过从零造轮子的坑,直接调用 CAccountInfo 与 CTrade 把仓位管理代码压到最短

含代码示例 第 2/3 篇
不少交易者照着旧 MQL4 习惯在 MQL5 里手写订单遍历与平仓,结果漏掉账户上下文检查,回测和实盘表现差出一截。标准交易类库早把这类脏活封装好了,硬写不仅慢还容易出隐性 bug。外汇贵金属杠杆高,这类逻辑错误会直接放大穿仓概率。

◍ 用 CTrade 摆弄挂单与持仓的状态读写

在 MT5 的 EA 里,先拿到持仓对象才能谈后续操作。用 myposition.Type() == POSITION_TYPE_BUY 判断方向只是第一步,真正有用的是把 Magic、手数、开仓价、SL、TP 一次性读出来,再用 StoreState() 把当前状态存快照,之后 CheckState() 返回 false 就说明仓位没变动,省得每帧都重算。 CTrade 是下单主力的封装类。设 Magic_No=12345 和 Deviation=20 点偏差后,OrderOpen 挂 SELLSTOP 时把挂单价压到 Bid()-_Point*550,也就是比实时买价低 550 点,这种距离在黄金上约 5.5 美元、欧美约 55 点,读者开 MT5 把 _Point 打印出来就能确认自己品种的实际跨度。 改挂单用 OrderModify 循环跑 OrdersTotal(),把 SL 钉在 Bid()+400 点、TP 在 Bid()-550 点,注意这是针对 SELLSTOP 的逆向止损逻辑。删单则从尾向前遍历 OrderDelete,避免索引错位。 真要直接开仓,PositionOpen 吃 Ask 价,SL=Ask-250 点、TP=Ask+500 点,手数写死 0.1。外汇和贵金属杠杆高,这种写死点数在跳空时可能直接穿损,参数务必按品种波动率手调。

MQL5 / C++
if (myposition.Type() == POSITION_TYPE_BUY)
{
class=class="str">"cmt">// This is a buy position
}
class="type">long pos_magic = myposition.Magic();
class="type">class="kw">double pos_vol = myposition.Volume(); class=class="str">"cmt">// Lots
class="type">class="kw">double pos_op_price = myposition.PriceOpen();
class="type">class="kw">double pos_stoploss = myposition.StopLoss();
class="type">class="kw">double pos_takeprofit = myposition.TakeProfit();
class=class="str">"cmt">// stores the current state of the position
myposition.StoreState();
if (!myposition.CheckState())
{
  class=class="str">"cmt">// position status has not changed yet
}
class="type">class="kw">string pos_symbol = myposition.Symbol();
class=class="str">"cmt">//An object of the CTrade class
CTrade mytrade;
class="type">class="kw">ulong Magic_No=class="num">12345;
mytrade.SetExpertMagicNumber(Magic_No);
class="type">class="kw">ulong Deviation=class="num">20;
mytrade.SetDeviationInPoints(Deviation);

class=class="str">"cmt">// define the class="kw">input parameters
class="type">class="kw">double Lots = class="num">0.1;
class="type">class="kw">double SL = class="num">0;
class="type">class="kw">double TP = class="num">0;
class=class="str">"cmt">// latest Bid price class="kw">using CSymbolInfo class object
class="type">class="kw">double Oprice = mysymbol.Bid()-_Point*class="num">550;
class=class="str">"cmt">// place(BuyStop) pending order
mytrade.OrderOpen(_Symbol,ORDER_TYPE_SELLSTOP,Lots,class="num">0.0,Oprice,
SL,TP,ORDER_TIME_GTC,class="num">0);
class=class="str">"cmt">// Select total orders in history and get total pending orders
class=class="str">"cmt">// (as shown within the COrderInfo class section). 
class=class="str">"cmt">// Use the CSymbolInfo class object to get the current ASK/BID price
class="type">int Stoploss = class="num">400;
class="type">int Takeprofit = class="num">550;
for(class="type">int j=class="num">0; j<OrdersTotal(); j++)
{
  class="type">class="kw">ulong o_ticket = OrderGetTicket(j);
  if(o_ticket != class="num">0)
  {
   class=class="str">"cmt">// Stoploss must have been defined
   class="type">class="kw">double SL = mysymbol.Bid() + Stoploss*_Point;
   class=class="str">"cmt">// Takeprofit must have been defined 
   class="type">class="kw">double TP = mysymbol.Bid() - Takeprofit*_Point; 
   class=class="str">"cmt">// lastest ask price class="kw">using CSymbolInfo class object
   class="type">class="kw">double Oprice = mysymbol.Bid();
class=class="str">"cmt">// modify pending BuyStop order
   mytrade.OrderModify(o_ticket,Oprice,SL,TP,ORDER_TIME_GTC,class="num">0);
  }
}
class=class="str">"cmt">// Select total orders in history and get total pending orders
class=class="str">"cmt">// (as shown within the COrderInfo class section). 
class="type">int o_total=OrdersTotal();
for(class="type">int j=o_total-class="num">1; j>=class="num">0; j--)
{
   class="type">class="kw">ulong o_ticket = OrderGetTicket(j);
   if(o_ticket != class="num">0)
   {
    class=class="str">"cmt">// class="kw">delete the pending Sell Stop order
    mytrade.OrderDelete(o_ticket);
   }
}
class=class="str">"cmt">// define the class="kw">input parameters and use the CSymbolInfo class
class=class="str">"cmt">// object to get the current ASK/BID price
class="type">class="kw">double Lots = class="num">0.1;
class=class="str">"cmt">// Stoploss must have been defined 
class="type">class="kw">double SL = mysymbol.Ask() - Stoploss*_Point; 
class=class="str">"cmt">//Takeprofit must have been defined 
class="type">class="kw">double TP = mysymbol.Ask() + Takeprofit*_Point; 
class=class="str">"cmt">// latest ask price class="kw">using CSymbolInfo class object
class="type">class="kw">double Oprice = mysymbol.Ask();
class=class="str">"cmt">// open a buy trade
mytrade.PositionOpen(_Symbol,ORDER_TYPE_BUY,Lots,
Oprice,SL,TP,"Test Buy");
if (myposition.Select(_Symbol))
{
  class="type">int newStoploss = class="num">250;
  class="type">int newTakeprofit = class="num">500;
 class="type">class="kw">double SL = mysymbol.Ask() - newStoploss*_Point;  

用 CTrade 类把六种下单方式一次跑通

在 MT5 的 EA 逻辑里,CTrade 对象(常命名为 mytrade)承担了所有下单、改单、平仓动作。下面这段把市价单、挂单、改仓、平仓的调用方式集中列出来,复制进 OnTick 或自定义函数就能直接验证。 市价买入时,若手动算 SL/TP,常以 Ask 为基准加减点数乘 _Point。例如固定 0.1 手,SL = Ask - Stoploss*_Point,TP = Ask + Takeprofit*_Point;若图省事,Buy(Lots,NULL,0.0,0.0,0.0,"Buy Trade") 里后三个 0.0 代表用市价、不挂止损止盈。 卖出方向相反,用 Bid 做基准:SL = Bid + Stoploss*_Point,TP = Bid - Takeprofit*_Point。外汇与贵金属杠杆高,实盘跑之前先把 Stoploss、Takeprofit 变量在外部参数里定义好,否则编译虽过,下单会被服务器拒。 挂单里 BuyStop 的价格常取前一根 K 线高点加 2 点再加点差:bprice = mrate[1].high + 2*_Point + sprd*_Point,再用 NormalizeDouble 按 _Digits 对齐。SellStop 则取 mrate[1].low - 2*_Point,不加点差。 BuyLimit 可设在 mrate[1].open - 5*_Point + sprd*_Point,先只传价格不下止损,后续用 PositionModify 补。改仓和清仓就一句:mytrade.PositionModify(_Symbol,SL,TP) 改止损止盈,mytrade.PositionClose(_Symbol) 平掉当前符号持仓。

MQL5 / C++
class="type">class="kw">double TP = mysymbol.Ask() + newTakeprofit*_Point;
class=class="str">"cmt">//modify the open position for this symbol
mytrade.PositionModify(_Symbol,SL,TP);
}
if (myposition.Select(_Symbol))
{
class=class="str">"cmt">//close the open position for this symbol
 mytrade.PositionClose(_Symbol);
}
class="type">class="kw">double Lots = class="num">0.1;
class=class="str">"cmt">// Stoploss must have been defined
class="type">class="kw">double SL = mysymbol.Ask() - Stoploss*_Point;
class=class="str">"cmt">//Takeprofit must have been defined
class="type">class="kw">double TP = mysymbol.Ask() +Takeprofit*_Point;
class=class="str">"cmt">// latest ask price class="kw">using CSymbolInfo class object
class="type">class="kw">double Oprice = mysymbol.Ask();
class=class="str">"cmt">// open a buy trade
mytrade.Buy(Lots,NULL,Oprice,SL,TP,"Buy Trade");
class=class="str">"cmt">//OR
mytrade.Buy(Lots,NULL,class="num">0.0,class="num">0.0,class="num">0.0,"Buy Trade");
class=class="str">"cmt">// modify position later
class="type">class="kw">double Lots = class="num">0.1;
class=class="str">"cmt">// Stoploss must have been defined
class="type">class="kw">double SL = mysymbol.Bid() + Stoploss*_Point;
class=class="str">"cmt">//Takeprofit must have been defined
class="type">class="kw">double TP = mysymbol.Bid() - Takeprofit*_Point;
class=class="str">"cmt">// latest bid price class="kw">using CSymbolInfo class object
class="type">class="kw">double Oprice = mysymbol.Bid();
class=class="str">"cmt">// open a Sell trade
mytrade.Sell(Lots,NULL,Oprice,SL,TP,"Sell Trade");
class=class="str">"cmt">//OR
mytrade.Sell(Lots,NULL,class="num">0.0,class="num">0.0,class="num">0.0,"Sell Trade");
class=class="str">"cmt">//(modify position later)
class="type">class="kw">double Lot = class="num">0.1;
class=class="str">"cmt">//Buy price = bar class="num">1 High + class="num">2 pip + spread
class="type">int sprd=mysymbol.Spread();
class="type">class="kw">double bprice =mrate[class="num">1].high + class="num">2*_Point + sprd*_Point;
class=class="str">"cmt">//--- Buy price
class="type">class="kw">double mprice=NormalizeDouble(bprice,_Digits);
class=class="str">"cmt">//--- Stop Loss
class="type">class="kw">double stloss = NormalizeDouble(bprice - STP*_Point,_Digits);
class=class="str">"cmt">//--- Take Profit
class="type">class="kw">double tprofit = NormalizeDouble(bprice+ TKP*_Point,_Digits);
class=class="str">"cmt">//--- open BuyStop order
mytrade.BuyStop(Lot,mprice,_Symbol,stloss,tprofit);
class="type">class="kw">double Lot = class="num">0.1;
class=class="str">"cmt">//--- Sell price = bar class="num">1 Low - class="num">2 pip
class=class="str">"cmt">//--- class="type">MqlRates mrate already declared
class="type">class="kw">double sprice=mrate[class="num">1].low-class="num">2*_Point;
class=class="str">"cmt">//--- SellStop price
class="type">class="kw">double slprice=NormalizeDouble(sprice,_Digits);
class=class="str">"cmt">//--- Stop Loss
class="type">class="kw">double ssloss=NormalizeDouble(sprice+STP*_Point,_Digits);
class=class="str">"cmt">//--- Take Profit
class="type">class="kw">double stprofit=NormalizeDouble(sprice-TKP*_Point,_Digits);
class=class="str">"cmt">//--- Open SellStop Order
mytrade.SellStop(Lot,slprice,_Symbol,ssloss,stprofit);
Usage:
class=class="str">"cmt">//--- Buy price = bar class="num">1 Open  - class="num">5 pip + spread
class="type">class="kw">double Lot = class="num">0.1;
class="type">int sprd=mysymbol.Spread();
class=class="str">"cmt">//--- symbol spread
class="type">class="kw">double bprice = mrate[class="num">1].open - class="num">5*_Point + sprd*_Point;
class=class="str">"cmt">//--- class="type">MqlRates mrate already declared
class="type">class="kw">double mprice=NormalizeDouble(bprice,_Digits);
class=class="str">"cmt">//--- BuyLimit price
class=class="str">"cmt">//--- place buyLimit order, modify stoploss and takeprofit later
mytrade.BuyLimit(Lot,mprice,_Symbol);
class=class="str">"cmt">//--- Sell Limit price = bar class="num">1 Open  + class="num">5 pip
class="type">class="kw">double Lot = class="num">0.1;
class=class="str">"cmt">//--- class="type">MqlRates mrate already declared

「挂单与成交结果的抓取姿势」

在 MT5 里用 CTrade 发挂单,先算价格再归一化是最容易踩坑的一步。下面这段代码以 SellLimit 为例:取前一根 K 线开盘价加 5 个点,用 NormalizeDouble 按品种精度截断后直接抛单,止损止盈留到后面再改。 double sprice = mrate[1].open + 5*_Point; //--- SellLimit double slprice=NormalizeDouble(sprice,_Digits); //place SellLimit order, modify stoploss and takeprofit later mytrade.SellLimit(Lot,slprice,_Symbol); 发完单不等于完事。立刻读 ResultRetcode、ResultDeal、ResultOrder、ResultPrice、ResultBid、ResultAsk,能把这次请求的真实回执抓出来;Alert 弹错误码和描述,比事后翻日志快。 int return_code = mytrade.ResultRetcode(); string ret_message = ResultRetcodeDescription(); Alert("Error code - " , mytrade.ResultRetcode() , "Error message - ", ret_message); long dl_ticket = mytrade.ResultDeal(); long o_ticket = mytrade.ResultOrder(); double o_volume = mytrade.ResultVolume(); double r_price = mytrade.ResultPrice(); double rq_bid = mytrade.ResultBid; double rq_ask = mytrade.ResultAsk; 想区分市价单和挂单,看 RequestAction 返回值:TRADE_ACTION_DEAL 是即时成交,TRADE_ACTION_PENDING 是挂单。RequestMagic、RequestOrder、RequestSymbol、RequestVolume、RequestPrice、RequestSL、RequestTP 这组能还原上一笔请求的下单参数,调试时直接 PrintRequest 和 PrintResult 对照即可。 if (mytrade.RequestAction() == TRADE_ACTION_DEAL) { } else if (mytrade.RequestAction() == TRADE_ACTION_PENDING) { } ulong mag_no = mytrade.RequestMagic(); ulong po_ticket = mytrade.RequestOrder(); string symb = mytrade.RequestSymbol(); double Lot = mytrade.RequestVolume(); double oprice = mytrade.RequestPrice(); double limitprice = mytrade.RequestStopLimit(); double sloss = mytrade.RequestSL(); double tprofit = mytrade.RequestTP(); ulong dev = mytrade.RequestDeviation(); 开市价多单时,Buy 返回 true 代表请求已递交或成交,弹窗打出成交价和成交量;返回 false 则把 RequestVolume、RequestSL、RequestTP、RequestPrice 和错误描述全打出来再 return。外汇和贵金属杠杆高,回执里的滑点和小数位误差可能让实际成交和预期差几个点,开 MT5 跑一遍这段就能看到自己品种的 _Digits 和 _Point 表现。 if(mytrade.Buy(Lot,_Symbol,mprice,stloss,tprofit)) { Alert("A Buy order at price:", mytrade.ResultPrice() , ", vol:",mytrade.ResultVolume()," has been successfully placed with deal Ticket#:\",mytrade.ResultDeal(),"!!"); mytrade.PrintResult(); } else { Alert("The Buy order request at vol:",mytrade.RequestVolume(), ", sl:", mytrade.RequestSL(),", tp:",mytrade.RequestTP(), ", price:", mytrade.RequestPrice(), " could not be completed -error:",mytrade.ResultRetcodeDescription()); mytrade.PrintRequest(); return; }

MQL5 / C++
class="type">class="kw">double sprice = mrate[class="num">1].open + class="num">5*_Point;
class=class="str">"cmt">//--- SellLimit
class="type">class="kw">double slprice=NormalizeDouble(sprice,_Digits);
class=class="str">"cmt">//place SellLimit order, modify stoploss and takeprofit later
mytrade.SellLimit(Lot,slprice,_Symbol);
class=class="str">"cmt">// a trade operation has just been carried out
class="type">int return_code = mytrade.ResultRetcode();
class="type">class="kw">string ret_message = ResultRetcodeDescription();
class=class="str">"cmt">// display it
Alert("Error code - " , mytrade.ResultRetcode() , "Error message - ", ret_message);
class="type">long dl_ticket = mytrade.ResultDeal();
class="type">long o_ticket = mytrade.ResultOrder();
class="type">class="kw">double o_volume = mytrade.ResultVolume();
class="type">class="kw">double r_price = mytrade.ResultPrice();
class="type">class="kw">double rq_bid = mytrade.ResultBid;
class="type">class="kw">double rq_ask = mytrade.ResultAsk;
class=class="str">"cmt">// after a trade operation
class=class="str">"cmt">// prints the trade request parameters
mytrade.PrintRequest();
class=class="str">"cmt">//prints the trade results
mytrade.PrintResult();
class=class="str">"cmt">//determine the Trade operation type for the last Trade request
if (mytrade.RequestAction() == TRADE_ACTION_DEAL)
{
  class=class="str">"cmt">// this is a market order for an immediate execution
}
else if (mytrade.RequestAction() == TRADE_ACTION_PENDING)
{
  class=class="str">"cmt">// this is a pending order.
}
class="type">class="kw">ulong mag_no = mytrade. RequestMagic();
class="type">class="kw">ulong po_ticket = mytrade.RequestOrder();
class="type">class="kw">string symb = mytrade.RequestSymbol();
class="type">class="kw">double Lot = mytrade.RequestVolume();
class="type">class="kw">double oprice = mytrade.RequestPrice();
class="type">class="kw">double limitprice = mytrade.RequestStopLimit();
class="type">class="kw">double sloss = mytrade.RequestSL();
class="type">class="kw">double tprofit = mytrade.RequestTP();
class="type">class="kw">ulong dev = mytrade.RequestDeviation();
if (mytrade.RequestType() == ORDER_TYPE_BUY)
{
  class=class="str">"cmt">// market order Buy was placed in the last request.
}
Print("The type of order placed in the last request is :",mytrade.RequestTypeDescription());
Print("The request action used in the last request is :",mytrade.RequestTypeDescription());
Print("The type of order filling policy used", " in the last request is :",RequestTypeFillingDescription());
class=class="str">"cmt">//--- open Buy position and check the result
if(mytrade.Buy(Lot,_Symbol,mprice,stloss,tprofit))
class=class="str">"cmt">//if(mytrade.PositionOpen(_Symbol,ORDER_TYPE_BUY,Lot,mprice,stloss,tprofit))
  { class=class="str">"cmt">//--- Request is completed or order placed
  Alert("A Buy order at price:", mytrade.ResultPrice() , ", vol:",mytrade.ResultVolume()," has been successfully placed with deal Ticket#:",mytrade.ResultDeal(),"!!");
  mytrade.PrintResult();
  }
else
  {
  Alert("The Buy order request at vol:",mytrade.RequestVolume(), ", sl:", mytrade.RequestSL(),", tp:",mytrade.RequestTP(), ", price:", mytrade.RequestPrice(), " could not be completed -error:",mytrade.ResultRetcodeDescription());
  mytrade.PrintRequest();
  class="kw">return;
}

◍ 用标准交易类库搭一个日线EA的骨架

想在 MT5 里跑一个能自动开仓、移损、平仓的 EA,最省事的办法是直接调用标准交易类库(CAccountInfo、CSymbolInfo、CPositionInfo、CTrade 等),而不是从零手写下单逻辑。下面这套结构在 GBPUSD 日线图上用缺省参数(获利 270、止损 100、追踪点 32)回测过,能直观看到仓位被修改的过程,但仅为验证类库用法,不代表实盘可盈利。 核心思路是先写一批自定义函数做「前置检查」:checkTrading 确认账户允许交易且行情与服务器同步、柱数够用;ConfirmMargin 按账户预付款百分比卡住下单额度;checkBuy / checkSell 用 MA 斜率加 ADX 的 +DI/-DI 交叉来判方向;checkClosePos / ClosePosition 负责按收盘价与 MA 关系平掉逆向仓;CheckModify / Modify 则在趋势延续时同步推止损和获利。OnInit 里设交易品种、幻数、偏离,并把指标句柄拿好;OnTick 每次跳价先跑 checkTrading,有新柱才复制缓冲、看有无敞口仓,再决定平、改或开。 挂单部分换 COrderInfo 上场:CountOrders 数当前同幻数同品种的挂单,DeletePending 把三柱前还没触发的废单清掉,保证同屏挂单不超三个。BUYSTOP 的开盘价取「柱1最高价 + 点差 + 2pip」,因为图表价是卖价,买向单得补点差才是买价;有效期用 ORDER_TIME_GTC,到期字段直接填 0。 外汇和贵金属杠杆高、滑点跳空频繁,这类 EA 在日线回测里看着规矩,真上 MT5 实盘前务必先开策略测试器用不同品种、不同 TP/SL/追踪点组合自己跑一遍,重点看平仓与移损触发是否如预期。

空头挂单触发后怎么把记录扒出来

SellStop 的下单逻辑和 BuyStop 对称:开盘价取 1 号 K 线最低价加 2 pip,这里不需要叠点差,因为卖单本来就是按卖价成交。订单类型写 ORDER_TYPE_SELL_STOP,复用同一套 OrderOpen / SellStop 函数即可,EA 跑完会在测试报告里给出图形与图表三张结果页。 挂单被触发那一刻,性质就变了:它不再是 pending order,而是转成一笔 deal。日志里能拆出三步——挂单等待、条件满足转交易、仓位建立,历史里同时留着 order 记录和 deal 记录两份数据。 想回查历史订单属性,直接把 CHistoryOrderInfo 类拉进来建个对象调方法;交易记录同理用对应的历史交易类。下面这段头文件与输入参数就是这类脚本的骨架,StopLoss=100、TakeProfit=240、EA_Magic=99977 这些字段都能在 MT5 里照抄验证。 外汇和贵金属杠杆高,回测跑顺不代表实盘能复现,挂单触发受滑点影响可能偏离 2 pip 设定。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|  Include ALL classes that will be used                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//--- The Trade Class
class="macro">#include <Trade\Trade.mqh>
class=class="str">"cmt">//--- The PositionInfo Class
class="macro">#include <Trade\PositionInfo.mqh>
class=class="str">"cmt">//--- The AccountInfo Class
class="macro">#include <Trade\AccountInfo.mqh>
class=class="str">"cmt">//--- The SymbolInfo Class
class="macro">#include <Trade\SymbolInfo.mqh>
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|  INPUT PARAMETERS                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="kw">input class="type">int      StopLoss=class="num">100;      class=class="str">"cmt">// Stop Loss
class="kw">input class="type">int      TakeProfit=class="num">240;   class=class="str">"cmt">// Take Profit
class="kw">input class="type">int      ADX_Period=class="num">15;    class=class="str">"cmt">// ADX Period
class="kw">input class="type">int      MA_Period=class="num">15;     class=class="str">"cmt">// Moving Average Period
class="kw">input class="type">class="kw">ulong    EA_Magic=class="num">99977;   class=class="str">"cmt">// EA Magic Number
class="kw">input class="type">class="kw">double   Adx_Min=class="num">24.0;     class=class="str">"cmt">// Minimum ADX Value
class="kw">input class="type">class="kw">double   Lot=class="num">0.1;          class=class="str">"cmt">// Lots to Trade
class="kw">input class="type">class="kw">ulong    dev=class="num">100;          class=class="str">"cmt">// Deviation 
class="kw">input class="type">long     Trail_point=class="num">32;   class=class="str">"cmt">// Points to increase TP/SL
class="kw">input class="type">int      Min_Bars = class="num">20;    class=class="str">"cmt">// Minimum bars required for Expert Advisor to trade
class="kw">input class="type">class="kw">double   TradePct = class="num">25;    class=class="str">"cmt">// Percentage of Account Free Margin to trade
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|  OTHER USEFUL PARAMETERS                                         |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int adxHandle; class=class="str">"cmt">// handle for our ADX indicator
class="type">int maHandle;  class=class="str">"cmt">// handle for our Moving Average indicator
class="type">class="kw">double plsDI[],minDI[],adxVal[]; class=class="str">"cmt">// Dynamic arrays to hold the values of +DI, -DI and ADX values for each bars
class="type">class="kw">double maVal[]; class=class="str">"cmt">// Dynamic array to hold the values of Moving Average for each bars
class="type">class="kw">double p_close; class=class="str">"cmt">// Variable to store the close value of a bar
class="type">int STP, TKP;  class=class="str">"cmt">// To be used for Stop Loss, Take Profit 
class="type">class="kw">double TPC;    class=class="str">"cmt">// To be used for Trade percent

「把交易闸门交给类对象去守」

EA 启动后先声明四个标准类对象:CTrade 管下单、CPositionInfo 读持仓、CAccountInfo 取账户、CSymbolInfo 拿品种状态。这套对象化写法比直接调系统函数更稳,也方便在多个函数里复用同一份账户与品种上下文。 checkTrading() 是总开关:只有账户允许交易、EA 允许交易、品种与服务器同步,且 Bars(_Symbol,_Period) 大于 Min_Bars 时才返回 true。Min_Bars 没给默认值,实盘前你必须在输入参数里设一个,比如 50,否则可能一根 K 线都不足就瞎跑。 ConfirmMargin() 用 MarginCheck(_Symbol,otype,Lot,price) 算开仓所需保证金,再用 FreeMargin() 取空闲保证金。判定逻辑是 MathFloor(空闲保证金*TPC) > MathFloor(所需保证金) 才放行,TPC 相当于自有资金占用上限系数,调小它能防过度杠杆。外汇与贵金属杠杆高,保证金计算偏差可能直接爆仓,务必先在策略测试器里用真实点值跑一遍。 方向判断分两个函数:checkBuy() 要求 maVal[0]>maVal[1]>maVal[2] 且收盘价在 maVal[1] 上方,同时 adxVal[1]>Adx_Min 且 plsDI[1]>minDI[1];checkSell() 对称反转。ADX 过滤弱趋势后,信号倾向只在有一定趋势强度时才触发。把这几段拷进 MT5 的 include 区,接上你的 OnTick 就能验证信号频率。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|  CREATE CLASS OBJECTS                                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//--- The Trade Class Object
CTrade mytrade;
class=class="str">"cmt">//--- The PositionInfo Class Object
CPositionInfo myposition;
class=class="str">"cmt">//--- The AccountInfo Class Object
CAccountInfo myaccount;
class=class="str">"cmt">//--- The SymbolInfo Class Object
CSymbolInfo mysymbol;
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|  Checks if our Expert Advisor can go ahead and perform trading     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool checkTrading()
{
   class="type">bool can_trade = class="kw">false;
   class=class="str">"cmt">// check if terminal is syncronized with server, etc
   if (myaccount.TradeAllowed() && myaccount.TradeExpert() && mysymbol.IsSynchronized())
   {
      class=class="str">"cmt">// do we have enough bars?
      class="type">int mbars = Bars(_Symbol,_Period);
      if(mbars >Min_Bars)
      {
         can_trade = true;
      }
   }
   class="kw">return(can_trade);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|  Confirms if margin is enough to open an order
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool ConfirmMargin(ENUM_ORDER_TYPE otype,class="type">class="kw">double price)
  {
   class="type">bool confirm = class="kw">false;
   class="type">class="kw">double lot_price = myaccount.MarginCheck(_Symbol,otype,Lot,price); class=class="str">"cmt">// Lot price/ Margin     
   class="type">class="kw">double act_f_mag = myaccount.FreeMargin();                          class=class="str">"cmt">// Account free margin 
   class=class="str">"cmt">// Check if margin required is okay based on setting
   if(MathFloor(act_f_mag*TPC)>MathFloor(lot_price))
    {
       confirm =true;
    }
    class="kw">return(confirm);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|  Checks for a Buy trade Condition                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool checkBuy()
{
   class="type">bool dobuy = class="kw">false;
   if ((maVal[class="num">0]>maVal[class="num">1]) && (maVal[class="num">1]>maVal[class="num">2]) &&(p_close > maVal[class="num">1]))
   {
      class=class="str">"cmt">// MA increases upwards and previous price closed above MA
      if ((adxVal[class="num">1]>Adx_Min)&& (plsDI[class="num">1]>minDI[class="num">1]))
      {
         class=class="str">"cmt">// ADX is greater than minimum and +DI is greater tha -DI for ADX
         dobuy = true;
      }
   }
   class="kw">return(dobuy);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|  Checks for a Sell trade Condition                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool checkSell()
{
   class="type">bool dosell = class="kw">false;
   if ((maVal[class="num">0]<maVal[class="num">1]) && (maVal[class="num">1]<maVal[class="num">2]) &&(p_close < maVal[class="num">1]))
   {
      class=class="str">"cmt">// MA decreases downwards and previuos price closed below MA
把类库诊断交给小布
小布盯盘已内置对 EA 调用标准类的常见异常扫描,打开对应品种页就能看到仓位修改是否被类库正确拦截,你只管调策略参数。

常见问题

CAccountInfo 负责读账户与标的属性,比如登录号、杠杆、点值;CTrade 负责发单、平仓、改止盈止损。前者只读,后者动作,混用会让代码职责不清。
类库内部做了 tick 与挂单状态的一致性处理,避免手写循环里竞态导致的重复发单,样本内表现更贴近实盘可能倾向。
外汇贵金属属高风险,本文示例仅展示类调用结构,未含风控与品种过滤,直接上实盘概率上会先撞滑点坑。
可以,小布盯盘的品种页会标记 EA 在平仓改仓时是否绕过 CTrade 直接调底层函数,这类绕过往往藏着回测不暴露的实盘故障。
位于 MQL5/Include/Trade 下,想在 IDE 外读源码就顺着这个目录翻,不建议改完直接覆盖原文件。