了解使用MQL5下单·综合运用
📈

了解使用MQL5下单·综合运用

(3/3)· 订单、头寸与交易术语厘清后,用两套API把开仓改单闭环跑通

含代码示例偏理论 第 3/3 篇
很多EA写到改单就卡壳,根源常是把订单、交易、头寸三个概念混为一谈。MT5工具箱里交易选项卡和历史记录的分布,其实已经暗示了它们的生命周期差异。先分清楚再写代码,能少踩一半坑。

◍ 挂单改删的 CTrade 直接调用

在 MQL5 里用标准库 CTrade 处理挂单,最直白的两条路就是 OrderModify 改参数、OrderDelete 撤单。下面这段调用把一张 ticket 对应的挂单从原设置改成新的限价与止损止盈,并指定了过期时间。 改单时传入的 1.07050 是成交价,SL 放在 1.07000、TP 放在 1.07150,距离成交价各 5 个点,属于偏紧的日内波段设置;过期类型用 ORDER_TIME_SPECIFIED,到期日钉死在 2023.08.31 00:00,到点未触发就自动失效。 如果判断逻辑反转,不必改单,直接 OrderDelete(ticket) 把挂单清掉即可。外汇与贵金属杠杆高,挂单未成交前也可能因跳空跳过触发价,实盘前建议在 MT5 策略测试器用历史数据跑一遍改删逻辑。

MQL5 / C++
  trade.OrderModify(
        ticket,                    class=class="str">"cmt">// ticket number of the pending order to modify
        class="num">1.07050,                   class=class="str">"cmt">// execution price
        class="num">1.07000,                   class=class="str">"cmt">// Stop Loss price
        class="num">1.07150,                   class=class="str">"cmt">// Take Profit price
        ORDER_TIME_SPECIFIED,      class=class="str">"cmt">// type by expiration
        D&class="macro">#x27;class="num">2023.08.class="num">31 class="num">00.00&class="macro">#x27;,       class=class="str">"cmt">// expiration
        class="num">0,                         class=class="str">"cmt">// StopLimit price
  );
  trade.OrderDelete(
        ticket,                    class=class="str">"cmt">// tick number of the pending order to class="kw">delete
  );

「用 CTrade 对象重写订单发送逻辑」

把基于 OrderSend() 的裸函数交易系统迁移到 CTrade 类,核心差异只在三处:预处理器引入 Trade.mqh、声明一个 CTrade 实例、用成员方法替换底层下单函数。其余指标计算与条件判断结构和前文保持一致,不需要重写。 买入触发时直接调 trade.Buy(0.1, _Symbol, Ask, Ask-500*_Point, Ask+1000*_Point, NULL),卖出则用 trade.Sell(0.1, _Symbol, Bid, Bid+500*_Point, Bid-1000*_Point, NULL)。这两个方法内部封装了订单校验与重发,比手动拼 OrderSend 参数更不容易因填错字段被拒单。 下面这段完整代码在 H1 周期上用 50 周期 SMA 做穿越:前一根收盘在均线下方且最新收盘站上均线则买,反向则卖;只在每根新 K 线出现时检查一次(靠 barsTotal 比对 bars 实现)。止损 500 点、止盈 1000 点按当前品种 _Point 动态换算,外汇与贵金属杠杆高、点值波动大,实盘前请在策略测试器用历史数据验证滑点与点差影响。 别把封装当省心 CTrade 帮你管了重发和校验,但仓位 0.1 手、止损 500 点这类硬参数仍是写死的。换到 XAUUSD 这种大点值品种,500 点止损可能对应几十美元风险,直接抄代码容易超你的资金承受度。

MQL5 / C++
class="macro">#include <Trade\Trade.mqh>
CTrade trade;
trade.Buy(class="num">0.1,_Symbol,Ask,Ask-(class="num">500*_Point),Ask+(class="num">1000*_Point),NULL);
trade.Sell(class="num">0.1,_Symbol,Bid,Bid+(class="num">500*_Point),Bid-(class="num">1000*_Point),NULL);
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                 CTrade_Trading_System.mq5 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#include <Trade\Trade.mqh>
class="type">int simpleMA;
class="type">int barsTotal;
CTrade trade;
class="type">int OnInit()
  {
   simpleMA = iMA(_Symbol, PERIOD_H1, class="num">50, class="num">0, MODE_SMA, PRICE_CLOSE);
   barsTotal=iBars(_Symbol,PERIOD_H1);
   class="kw">return(INIT_SUCCEEDED);
  }
class="type">void OnTick()
  {
   class="type">MqlRates priceArray[];
   class="type">class="kw">double mySMAArray[];
   class="type">class="kw">double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
   class="type">class="kw">double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);
   ArraySetAsSeries(priceArray,true);
   ArraySetAsSeries(mySMAArray,true);
   class="type">int Data=CopyRates(_Symbol,_Period,class="num">0,class="num">3,priceArray);
   CopyBuffer(simpleMA,class="num">0,class="num">0,class="num">3,mySMAArray);
   class="type">class="kw">double lastClose=(priceArray[class="num">1].close);
   class="type">class="kw">double prevClose=(priceArray[class="num">2].close);
   class="type">class="kw">double SMAVal = NormalizeDouble(mySMAArray[class="num">1],_Digits);
   class="type">class="kw">double prevSMAVal = NormalizeDouble(mySMAArray[class="num">2],_Digits);
   class="type">int bars=iBars(_Symbol,PERIOD_H1);
   if(barsTotal != bars)
     {
      barsTotal=bars;
      if(prevClose<prevSMAVal && lastClose>SMAVal)
        {
         trade.Buy(class="num">0.1,_Symbol,Ask,Ask-(class="num">500*_Point),Ask+(class="num">1000*_Point),NULL);
        }
      if(prevClose>prevSMAVal && lastClose<SMAVal)
        {
         trade.Sell(class="num">0.1,_Symbol,Bid,Bid+(class="num">500*_Point),Bid-(class="num">1000*_Point),NULL);
        }
     }
  }

落代码前先过一遍这两套样例

前面两篇分别给了 OrderSend 与 CTrade 两套移动平均交叉系统的完整 mq5 文件:OrderSend_Trading_system.mq5 约 2.17 KB,CTrade_Trading_System.mq5 约 1.45 KB。两者实现同一逻辑,差异只在下单接口层——CTrade 类把仓位、挂单、止损止盈的维护封装掉了,写起来明显省事。 如果你打算直接拿这两个文件去跑,先记住一件事:它们只是用来对照两种接口写法的演示件,没有经过你的参数适配和回测。外汇和贵金属杠杆高、滑点随机,任何未验证的 EA 上真实账户都可能快速亏掉本金。 真要落地,建议先在策略测试器里用 2020—2023 的 tick 数据跑一遍,把均线周期和手数按自己品种调完再看成交日志。MT5 里按 F4 编译、F5 启动测试,比读十遍说明都直观。

把下单诊断交给小布盯盘
这些订单状态与持仓变动的识别,小布盯盘的AIGC已内置,打开对应品种页即可看到实时仓位结构与挂单分布,你只管把策略逻辑写干净。

常见问题

市场订单以当前价立即成交,挂单需指定触发价与有效期;二者都通过OrderSend或CTrade提交,但挂单后续要用修改或删除订单函数维护。
MT5中持仓的止损止盈是以挂单形式挂出的,修改止损实质是修改对应挂单,因此需走订单修改接口而非直接动成交记录。
小布盯盘侧重盘面与持仓可视化,不编译EA,但你能对照它的实时订单面板验证自己代码发出的指令是否符合预期。
CTrade封装了重试、结果校验与错误码处理,写出的开仓改单代码更短且容错更好,适合把精力放在策略而非底层通信。