MQL5 中的高级订单执行算法:TWAP、VWAP 和冰山订单(基础篇)
📘

MQL5 中的高级订单执行算法:TWAP、VWAP 和冰山订单(基础篇)

第 1/3 篇

MT5里三类执行算法的真身

在 MetaTrader 5 的 MQL5 环境里,TWAP(时间加权均价)、VWAP(成交量加权均价)和冰山订单并不是经纪商默认给零售户的按钮,而是需要用代码自己搭的执行算法。它们的共同目的,是把一笔大单拆开,降低对盘口的冲击,但拆法逻辑完全不同:TWAP 按固定时间间隔均匀发单,VWAP 盯着成交量分布发单,冰山则只露出一个小头寸、其余挂藏单。 原文给出的系列框架里,这一节先点出后续要落地的四个模块:MQL5 基础实现、性能分析器(Profiler)跑法、算法间横向比较、以及把执行算法缝进交易策略的回测示例。该系列发布于 2026 年 1 月 16 日,原文标注阅读量 902、评论 6,属于刚铺开的技术向内容,还没到收益结论阶段。 对外汇与贵金属交易者而言,这类算法在低点差主流品种上意义有限,在流动性薄、滑点大的交叉盘或异常时段才可能显出优势;自行部署有代码风险与执行失效风险,务必先在策略测试器里用历史数据验证。

「散户也能用的机构级执行思路」

机构用了几十年的算法拆单,核心目的只有一个:避开自己的单子把价格打歪。市价单一把梭,哪怕在欧美盘流动性最好的时段,也会因为订单簿被瞬间抽空而产生额外滑点,这种拖累在震荡市里更明显。 MQL5 把这套逻辑下放到了个人桌面。你不需要管账户里是 0.5 手还是 5 手,波动性对执行的侵蚀都存在;用 TWAP、VWAP、Iceberg 这类策略把大单拆小、错时投放,滑点倾向被压低,平均成交价可能比一次性介入更优。 冰山单(Iceberg)只挂出真实量的零头,剩下藏在后台,盯着盘口的嗅单算法很难猜出你的全貌。这套东西以前是百万预算团队的专利,现在写几段 MQL5 接进 MT5 就能跑。外汇与贵金属杠杆高、波动急,拆单只降低冲击概率,不消除方向风险。

◍ TWAP、VWAP 与冰山单的实战适用边界

大单拆细不是装饰性操作,核心目标是把市场冲击摊薄、把滑点压低。MT5 自营算法或第三方 EA 里最常见的三种基础执行逻辑,各自对应不同的流动性环境,选错场景比不拆更伤。 TWAP 把总单量按固定时间间隔切成等份发出,全程不盯价格波动,只在设定起止时间内匀速落地。它适合流动性平稳、你只求某时间段平均成交价的场景;若硬套在亚盘尾段流动性骤降的 XAUUSD,固定节奏反而容易在稀疏盘口吃大滑点。外汇与贵金属杠杆高、跳空频繁,这类机械节奏需手动避开数据窗。 VWAP 比 TWAP 多一层成交量加权:依据历史成交量曲线分配每笔大小,量大的时段多跑、量小的时段少跑,部分实现还能读实时量能微调。EURUSD 日内量能呈双峰(伦敦+纽约重叠段明显放大),顺这个分布挂单,冲击成本倾向低于均分法。 冰山单走另一条路——只露尖角,底层挂单隐藏。每成交一小块再放下一小块,可见量可固定也可随机抖动以防被嗅探;通常用限价单保价格。在流动性薄的 cross 货币对或夜盘贵金属里,想在不惊动盘口的前提下出掉大仓,这是更稳的隐蔽通道,但成交耗时不可控,需接受可能拖过你预期窗口。

把执行算法写成可复用的 MQL5 类

做拆单执行系统,先别急着堆策略,把共性抽进一个基类最省事。CExecutionAlgorithm 管的是全算法都要用的状态:总成交量、已成交、剩余量、起止时间、滑点、成交均价。派生类只重写自己的下单节奏,基类负责记账和发单。 PlaceOrder 是基类的核心发单函数。它对 MqlTradeRequest 和 MqlTradeResult 做零初始化,填交易品种、成交量、订单类型、价格、滑点和幻数,然后调 OrderSend 发市价单。若发送失败或经纪商返回码不是 TRADE_RETCODE_DONE,记错误并返回 false;成功才更新总单/已成单计数、已执行与剩余量,重算均价并拿单号返回 true。 TWAP 把 m_startTime 到 m_endTime 切成 m_intervals 个等长段。CalculateNextExecutionTime 首调返回 m_startTime + m_initialDelay,后续返回 TimeCurrent() + 一个间隔秒数,且绝不越过 m_endTime。Execute 到点从剩余量切固定或随机小块,查卖价(买)或买价(卖),发完单推进间隔计数、标首单已发,并排下一次函数或在区间/量用完时停策略。 VWAP 与 TWAP 结构相近,但按历史成交量分布配权重,不是均分。冰山单走挂单路线:Execute 用 TRADE_ACTION_PENDING 只挂可见切片,成功存单号并标活动;Update 按 m_checkInterval 轮询,超时则撤单、休眠 m_orderPlacementDelay 再补新切片。外汇与贵金属杠杆高,这类算法只降市场冲击,不消除价格反向风险,参数请在策略测试器用真实点差验证。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Base class for all execution algorithms                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CExecutionAlgorithm
{
class="kw">protected:
   class="type">class="kw">string            m_symbol;            class=class="str">"cmt">// Symbol to trade
   class="type">class="kw">double            m_totalVolume;       class=class="str">"cmt">// Total volume to execute
   class="type">class="kw">double            m_executedVolume;    class=class="str">"cmt">// Volume already executed
   class="type">class="kw">double            m_remainingVolume;   class=class="str">"cmt">// Volume remaining to execute
   class="type">class="kw">datetime          m_startTime;         class=class="str">"cmt">// Start time for execution
   class="type">class="kw">datetime          m_endTime;           class=class="str">"cmt">// End time for execution
   class="type">bool              m_isActive;          class=class="str">"cmt">// Flag indicating if the algorithm is active
   class="type">int               m_totalOrders;       class=class="str">"cmt">// Total number of orders placed
   class="type">int               m_filledOrders;      class=class="str">"cmt">// Number of filled orders
   class="type">class="kw">double            m_avgExecutionPrice; class=class="str">"cmt">// Average execution price
   class="type">class="kw">double            m_executionValue;    class=class="str">"cmt">// Total value of executed orders
   class="type">int               m_slippage;          class=class="str">"cmt">// Allowed slippage in points

class="kw">public:
   class=class="str">"cmt">// Constructor
   CExecutionAlgorithm(class="type">class="kw">string symbol, class="type">class="kw">double volume, class="type">class="kw">datetime startTime, class="type">class="kw">datetime endTime, class="type">int slippage = class="num">3);

   class=class="str">"cmt">// Destructor
   class="kw">virtual ~CExecutionAlgorithm();

   class=class="str">"cmt">// Common methods
   class="kw">virtual class="type">bool      Initialize();

「执行算法类的接口与下单实现」

在 MT5 的自定义执行算法里,基类的纯虚函数 Execute() 和 Update() 强制子类给出具体逻辑,而 Terminate() 给了默认空实现,意味着你可以只重写需要介入的环节。GetSymbol、GetTotalVolume 这类 const 内联 getter 直接返回成员变量,调用开销极低,适合在每tick里频繁读状态。 下单核心落在 PlaceOrder(),它封装了 MqlTradeRequest 的填充与 OrderSend 调用。注意 request.magic 写死为 123456,这只是示例用的识别码,实盘里建议改成随策略实例变化的动态值,否则多 EA 同时跑会分不清成交归属。 每次成功成交后,m_totalOrders 与 m_filledOrders 都自增,m_executedVolume 累加、m_remainingVolume 扣减,并调用 UpdateAverageExecutionPrice 刷新均价。若 OrderSend 返回 false 或 retcode 不等于 TRADE_RETCODE_DONE,会 Print 错误码并退出,不更新统计——这套计数逻辑让你在回测或实盘里能直接读出算法吃单的完成度。外汇与贵金属杠杆高,滑点字段 m_slippage 设宽了容易成交在劣价,设窄了可能连单都丢,开 MT5 把这段贴进类里改 magic 和 deviation 跑一遍就知道了。

MQL5 / C++
class="kw">virtual class="type">bool        Execute() = class="num">0;
  class="kw">virtual class="type">bool        Update() = class="num">0;
  class="kw">virtual class="type">bool        Terminate();

  class=class="str">"cmt">// Utility methods
  class="type">bool                PlaceOrder(ENUM_ORDER_TYPE orderType, class="type">class="kw">double volume, class="type">class="kw">double price);
  class="type">bool                CancelOrder(class="type">ulong ticket);
  class="type">void                UpdateAverageExecutionPrice(class="type">class="kw">double price, class="type">class="kw">double volume);

  class=class="str">"cmt">// Getters
  class="type">class="kw">string              GetSymbol() const { class="kw">return m_symbol; }
  class="type">class="kw">double              GetTotalVolume() const { class="kw">return m_totalVolume; }
  class="type">class="kw">double              GetExecutedVolume() const { class="kw">return m_executedVolume; }
  class="type">class="kw">double              GetRemainingVolume() const { class="kw">return m_remainingVolume; }
  class="type">class="kw">datetime            GetStartTime() const { class="kw">return m_startTime; }
  class="type">class="kw">datetime            GetEndTime() const { class="kw">return m_endTime; }
  class="type">bool                IsActive() const { class="kw">return m_isActive; }
  class="type">int                 GetTotalOrders() const { class="kw">return m_totalOrders; }
  class="type">int                 GetFilledOrders() const { class="kw">return m_filledOrders; }
  class="type">class="kw">double              GetAverageExecutionPrice() const { class="kw">return m_avgExecutionPrice; }
};
class="type">bool CExecutionAlgorithm::PlaceOrder(ENUM_ORDER_TYPE orderType, class="type">class="kw">double volume, class="type">class="kw">double price)
{
  class=class="str">"cmt">// Prepare the trade request
  class="type">MqlTradeRequest request;
  class="type">MqlTradeResult result;
  ZeroMemory(request);
  ZeroMemory(result);

  request.action = TRADE_ACTION_DEAL;
  request.symbol = m_symbol;
  request.volume = volume;
  request.type = orderType;
  request.price = price;
  request.deviation = m_slippage;
  request.magic = class="num">123456; class=class="str">"cmt">// Magic number for identification

  class=class="str">"cmt">// Send the order
  class="type">bool success = OrderSend(request, result);

  if(!success)
  {
    Print("OrderSend error: ", GetLastError());
    class="kw">return false;
  }

  class=class="str">"cmt">// Check the result
  if(result.retcode != TRADE_RETCODE_DONE)
  {
    Print("OrderSend failed with code: ", result.retcode);
    class="kw">return false;
  }

  class=class="str">"cmt">// Update statistics
  m_totalOrders++;
  m_filledOrders++;
  m_executedVolume += volume;
  m_remainingVolume -= volume;
  UpdateAverageExecutionPrice(price, volume);

  class=class="str">"cmt">// Store the order ticket for future reference
  class="type">ulong ticket = result.order;

◍ TWAP 类把时间切片拆成等权成交

CTWAP 继承自 CExecutionAlgorithm,核心思路是把总成交量按时间均分到 m_intervals 个区间,每个区间发 m_intervalVolume 的手数。构造函数里默认 initialDelay=10 秒,意味着算法启动后不会立刻吃单,先等 10 秒再看盘口。 成员变量里 m_useRandomization 和 m_randomizationFactor(默认 0.2)决定每笔是否做 ±20% 的量级抖动,用来降低被识别为机械单的概率。m_checkInterval 控制轮询成交状态的频率,避免每 tick 都去查订单拖慢 EA。 CalculateNextExecutionTime 先算 totalSeconds = m_endTime - m_startTime,再除 m_intervals 得到 intervalSeconds。这套切法在流动性平的时段能把冲击成本摊薄,但外汇与贵金属杠杆高、滑点跳变快,实际执行偏离 TWAP 理论价的可能不小,回测和实盘都建议先开 MT5 用Tick数据跑一遍。

MQL5 / C++
class CTWAP : class="kw">public CExecutionAlgorithm
{
class="kw">private:
  class="type">int               m_intervals;          class=class="str">"cmt">// Number of time intervals
  class="type">int               m_currentInterval;    class=class="str">"cmt">// Current interval
  class="type">class="kw">datetime          m_nextExecutionTime;  class=class="str">"cmt">// Next execution time
  class="type">class="kw">double            m_intervalVolume;     class=class="str">"cmt">// Volume per interval
  class="type">bool              m_useRandomization;   class=class="str">"cmt">// Whether to randomize order sizes
  class="type">class="kw">double            m_randomizationFactor; class=class="str">"cmt">// Factor for randomization(class="num">0-class="num">1)
  ENUM_ORDER_TYPE   m_orderType;          class=class="str">"cmt">// Order type(buy or sell)
  class="type">bool              m_firstOrderPlaced;   class=class="str">"cmt">// Flag to track if first order has been placed
  class="type">int               m_initialDelay;       class=class="str">"cmt">// Initial delay in seconds before first execution
  class="type">class="kw">datetime          m_lastCheckTime;      class=class="str">"cmt">// Last time order status was checked
  class="type">int               m_checkInterval;      class=class="str">"cmt">// How often to check order status(seconds)

class="kw">public:
  class=class="str">"cmt">// Constructor
  CTWAP(class="type">class="kw">string symbol, class="type">class="kw">double volume, class="type">class="kw">datetime startTime, class="type">class="kw">datetime endTime,
        class="type">int intervals, ENUM_ORDER_TYPE orderType, class="type">bool useRandomization = false,
        class="type">class="kw">double randomizationFactor = class="num">0.2, class="type">int slippage = class="num">3, class="type">int initialDelay = class="num">10);

  class=class="str">"cmt">// Implementation of class="kw">virtual methods
  class="kw">virtual class="type">bool      Initialize() class="kw">override;
  class="kw">virtual class="type">bool      Execute() class="kw">override;
  class="kw">virtual class="type">bool      Update() class="kw">override;
  class="kw">virtual class="type">bool      Terminate() class="kw">override;

  class=class="str">"cmt">// TWAP specific methods
  class="type">void              CalculateIntervalVolume();
  class="type">class="kw">datetime          CalculateNextExecutionTime();
  class="type">class="kw">double            GetRandomizedVolume(class="type">class="kw">double baseVolume);
  class="type">bool              IsTimeToExecute();
};

class="type">class="kw">datetime CTWAP::CalculateNextExecutionTime()
{
  class=class="str">"cmt">// Calculate the duration of each interval
  class="type">int totalSeconds = (class="type">int)(m_endTime - m_startTime);
  class="type">int intervalSeconds = totalSeconds / m_intervals;

  class=class="str">"cmt">// Calculate the next execution time
  class="type">class="kw">datetime nextTime;

常见问题

把总委托量拆成小切片,只挂出其中一小部分,成交后自动补单;切片大小建议低于该品种近5分钟平均单笔成交量的30%。
成交量平稳的亚盘时段用TWAP等权切片即可;美盘数据行情波动大,VWAP按成交量加权更能贴近真实盘口、减少冲击。
小布已内置这几类执行算法的策略模板,打开对应品种页选好总手数和时长,就能直接生成分批下单计划并监控补单。
至少实现初始化参数、切片计算、下单执行和状态回检四个方法;缺回检会导致补单逻辑死循环。
秒级切片在流动性差的品种上常遇拒单,建议最小切片间隔设为该品种 Tick median 间隔的2倍以上再回测。