MQL5 中的高级订单执行算法:TWAP、VWAP 和冰山订单·进阶篇
📘

MQL5 中的高级订单执行算法:TWAP、VWAP 和冰山订单·进阶篇

第 2/3 篇

TWAP 执行引擎的时间与手数逻辑

TWAP 拆单的核心在两段:计算下一笔触发时间,以及按区间投放手数。首区间用起始时间加初始延迟定位,后续区间则锚定 TimeCurrent() 往后推 intervalSeconds,且硬卡 m_endTime 不越界——这套写法能保证在 MT5 实盘里不会因重连或休眠导致节奏漂移。 Execute() 里先判 m_isActive 与 IsTimeToExecute(),未到点直接 return true 让定时器继续空转。手数方面,若开启随机化就走 GetRandomizedVolume(m_intervalVolume),否则吃固定 m_intervalVolume,并强制截断到 m_remainingVolume 以内,避免末段超卖。 下单走 MqlTradeRequest 直发,买价取 SYMBOL_ASK、卖价取 SYMBOL_BID,deviation 绑 m_slippage,magic 写死 123456 便于在账户历史里筛单。外汇与贵金属杠杆高、滑点跳空频繁,TWAP 只降低时机暴露,不消除方向性亏损可能,参数请在策略测试器先跑再上实盘。

MQL5 / C++
if(m_currentInterval == class="num">0) {
   class=class="str">"cmt">// First interval - start at the defined start time plus initial delay
   nextTime = m_startTime + m_initialDelay;
   
   Print("TWAP: First execution time calculated with ", m_initialDelay,
         " seconds delay: ", TimeToString(nextTime));
} else {
   class=class="str">"cmt">// For subsequent intervals, ensure proper spacing from current time
   class="type">class="kw">datetime currentTime = TimeCurrent();
   nextTime = currentTime + intervalSeconds;
   
   class=class="str">"cmt">// Make sure we don&class="macro">#x27;t exceed the end time
   if(nextTime > m_endTime)
      nextTime = m_endTime;
      
   Print("TWAP: Next execution time calculated: ", TimeToString(nextTime),
         " (interval: ", intervalSeconds, " seconds)");
}

class="kw">return nextTime;
}
class="type">bool CTWAP::Execute()
{
   if(!m_isActive)
      class="kw">return false;

   class=class="str">"cmt">// Check if it&class="macro">#x27;s time to execute the next order
   if(!IsTimeToExecute())
      class="kw">return true; class=class="str">"cmt">// Not time yet

   class=class="str">"cmt">// Calculate the volume for this execution
   class="type">class="kw">double volumeToExecute = m_useRandomization ?
                                 GetRandomizedVolume(m_intervalVolume) :
                                 m_intervalVolume;
                                 
   class=class="str">"cmt">// Ensure we don&class="macro">#x27;t exceed the remaining volume
   if(volumeToExecute > m_remainingVolume)
      volumeToExecute = m_remainingVolume;
      
   class=class="str">"cmt">// Get current market price
   class="type">class="kw">double price = class="num">0.0;
   if(m_orderType == ORDER_TYPE_BUY)
      price = SymbolInfoDouble(m_symbol, SYMBOL_ASK);
   else
      price = SymbolInfoDouble(m_symbol, SYMBOL_BID);
      
   Print("TWAP: Placing order for interval ", m_currentInterval,
         ", Volume: ", DoubleToString(volumeToExecute, class="num">2),
         ", Price: ", DoubleToString(price, _Digits));
         
   class=class="str">"cmt">// Place the order using OrderSend directly for more control
   class="type">MqlTradeRequest request;
   class="type">MqlTradeResult result;
   ZeroMemory(request);
   ZeroMemory(result);

   request.action = TRADE_ACTION_DEAL;
   request.symbol = m_symbol;
   request.volume = volumeToExecute;
   request.type = m_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("TWAP: OrderSend error: ", GetLastError());
      class="kw">return false;
   }

   class=class="str">"cmt">// Check the result

◍ TWAP 执行后的状态更新与 VWAP 类骨架

TWAP 算法在单笔子单发送完成后,先判断 retcode 是否为 TRADE_RETCODE_DONE,非完成态直接打印错误码并返回 false,避免脏状态继续推进。 若成交成功,则累加 m_totalOrders 与 m_filledOrders,并把本次 volumeToExecute 计入 m_executedVolume,同时从 m_remainingVolume 扣减。m_currentInterval 自增、m_firstOrderPlaced 置 true,这是后续判断「是否首单已发」的依据。 下一笔触发时刻由 CalculateNextExecutionTime() 算出,前提是当前区间数小于总区间且剩余量大于 0;否则 m_isActive 置 false,代表拆分全部跑完或无量可跑。Print 里用 DoubleToString(volumeToExecute,2) 和 _Digits 精度输出成交量与价格,方便在 MT5 Experts 日志里核对每段执行痕迹。 VWAP 类 CVWAP 公有继承自 CExecutionAlgorithem,私有成员里 m_volumeProfile[] 与 m_intervalVolumes[] 承载历史成交量剖面,m_adaptiveMode 控制是否切到实时量调节,m_historyDays 决定剖面对齐几天历史,m_profileLoaded 标记剖面是否成功载入。 别把 TWAP 的状态机直接套给 VWAP 两套算法的剩余量扣减逻辑一致,但 VWAP 的每区间手数来自剖面权重而非均分;若复用 TWAP 的 m_nextExecutionTime 推算而不重载,会出现前密后疏的错拍。

MQL5 / C++
if(result.retcode != TRADE_RETCODE_DONE)
  {
    Print("TWAP: OrderSend failed with code: ", result.retcode);
    class="kw">return false;
  }

  class=class="str">"cmt">// Update statistics
  m_totalOrders++;
  m_filledOrders++;
  m_executedVolume += volumeToExecute;
  m_remainingVolume -= volumeToExecute;

  class=class="str">"cmt">// Update interval counter
  m_currentInterval++;
  m_firstOrderPlaced = true;

  class=class="str">"cmt">// Calculate the time for the next execution
  if(m_currentInterval < m_intervals && m_remainingVolume > class="num">0)
    m_nextExecutionTime = CalculateNextExecutionTime();
  else
    m_isActive = false; class=class="str">"cmt">// All intervals completed or no volume left

  Print("TWAP: Executed ", DoubleToString(volumeToExecute, class="num">2),
        " at price ", DoubleToString(price, _Digits),
        ". Remaining: ", DoubleToString(m_remainingVolume, class="num">2),
        ", Next execution: ", TimeToString(m_nextExecutionTime));

  class="kw">return true;
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Volume-Weighted Average Price(VWAP) Algorithm                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CVWAP : 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_volumeProfile[];    class=class="str">"cmt">// Historical volume profile
  class="type">class="kw">double             m_intervalVolumes[];  class=class="str">"cmt">// Volume per interval based on profile
  class="type">bool               m_adaptiveMode;       class=class="str">"cmt">// Whether to adapt to real-time volume
  ENUM_ORDER_TYPE    m_orderType;          class=class="str">"cmt">// Order type(buy or sell)
  class="type">int               m_historyDays;         class=class="str">"cmt">// Number of days to analyze for volume profile
  class="type">bool               m_profileLoaded;      class=class="str">"cmt">// Flag indicating if profile was loaded
  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

「CVWAP 的执行节奏与冰山单骨架」

CVWAP 类的构造函数暴露了算法拆单的核心参数:默认回看 5 天历史成交、自适应模式开启、滑点 3 点、首单延迟 10 秒。把这些值直接丢进 MT5 策略测试器,能复现文末的时段切分逻辑。 CalculateNextExecutionTime 决定了母单在每个区间的触发点。首区间用 startTime + initialDelay 起步,之后每个区间按 (endTime-startTime)/intervals 的秒数顺推,且用 TimeCurrent() 做实时锚定,越界则钳制到 endTime。

MQL5 / C++
CVWAP(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">int historyDays = class="num">5,
           class="type">bool adaptiveMode = true, 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">// VWAP specific methods
class="type">bool              LoadVolumeProfile();
class="type">void              CalculateIntervalVolumes();
class="type">void              AdjustToRealTimeVolume();
class="type">class="kw">datetime          CalculateNextExecutionTime();
class="type">class="kw">double            GetCurrentVWAP();
class="type">bool              IsTimeToExecute();
};
class="type">class="kw">datetime CVWAP::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;

   if(m_currentInterval == class="num">0) {
      class=class="str">"cmt">// First interval - start at the defined start time plus initial delay
      nextTime = m_startTime + m_initialDelay;

      Print("VWAP: First execution time calculated with ", m_initialDelay,
            " seconds delay: ", TimeToString(nextTime));
   } else {
      class=class="str">"cmt">// For subsequent intervals, ensure proper spacing from current time
      class="type">class="kw">datetime currentTime = TimeCurrent();
      nextTime = currentTime + intervalSeconds;

      class=class="str">"cmt">// Make sure we don&class="macro">#x27;t exceed the end time
      if(nextTime > m_endTime)
         nextTime = m_endTime;

      Print("VWAP: Next execution time calculated: ", TimeToString(nextTime),
            " (interval: ", intervalSeconds, " seconds)");
   }

   class="kw">return nextTime;
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Iceberg Order Implementation                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CIcebergOrder : class="kw">public CExecutionAlgorithm
{
class="kw">private:
   class="type">class="kw">double            m_visibleVolume;       class=class="str">"cmt">// Visible portion of the order
   class="type">class="kw">double            m_minVisibleVolume;    class=class="str">"cmt">// Minimum visible volume
}
上面代码逐行拆解:构造参数里 historyDays=5 代表默认抓取近 5 日分时成交量;adaptiveMode=true 让算法按实时盘口修正权重;initialDelay=10 是首单防拥堵的静默期。CalculateNextExecutionTime 中 totalSeconds 强转 int 会丢毫秒,但 MT5 的 datetime 本就是秒级,无精度损失。 冰山单 CIcebergOrder 仅露出 m_visibleVolume 与 m_minVisibleVolume 两个私有字段,说明隐藏量 = 总委托 - 可见量,最小值兜底防完全不可见。外汇与贵金属杠杆高、滑点跳变频繁,这类算法在流动性稀薄时段可能触发异常撤单,上实盘前务必在策略测试器跑满不同会话。

MQL5 / C++
CVWAP(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">int historyDays = class="num">5,
           class="type">bool adaptiveMode = true, 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">// VWAP specific methods
class="type">bool              LoadVolumeProfile();
class="type">void              CalculateIntervalVolumes();
class="type">void              AdjustToRealTimeVolume();
class="type">class="kw">datetime          CalculateNextExecutionTime();
class="type">class="kw">double            GetCurrentVWAP();
class="type">bool              IsTimeToExecute();
};
class="type">class="kw">datetime CVWAP::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;

   if(m_currentInterval == class="num">0) {
      class=class="str">"cmt">// First interval - start at the defined start time plus initial delay
      nextTime = m_startTime + m_initialDelay;

      Print("VWAP: First execution time calculated with ", m_initialDelay,
            " seconds delay: ", TimeToString(nextTime));
   } else {
      class=class="str">"cmt">// For subsequent intervals, ensure proper spacing from current time
      class="type">class="kw">datetime currentTime = TimeCurrent();
      nextTime = currentTime + intervalSeconds;

      class=class="str">"cmt">// Make sure we don&class="macro">#x27;t exceed the end time
      if(nextTime > m_endTime)
         nextTime = m_endTime;

      Print("VWAP: Next execution time calculated: ", TimeToString(nextTime),
            " (interval: ", intervalSeconds, " seconds)");
   }

   class="kw">return nextTime;
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Iceberg Order Implementation                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CIcebergOrder : class="kw">public CExecutionAlgorithm
{
class="kw">private:
   class="type">class="kw">double            m_visibleVolume;       class=class="str">"cmt">// Visible portion of the order
   class="type">class="kw">double            m_minVisibleVolume;    class=class="str">"cmt">// Minimum visible volume

冰山订单类的成员与接口骨架

下面这段声明定义了一个冰山订单类(CIcebergOrder)的私有状态与公开接口,核心是把大单拆成可见小块挂出,降低对盘口的暴露。外汇与贵金属市场流动性瞬变,这类拆单逻辑只降低被追踪的概率,不保证成交价更优,实盘须自担滑点风险。 私有成员里,m_maxVisibleVolume 与 m_useRandomVisibleVolume 控制单笔可见上限与是否随机化;m_orderPlacementDelay 默认 1000 毫秒,即两次挂单最小间隔;m_avoidRoundNumbers 配合 m_priceDeviation(默认 2 点)把挂单价推离整数位。m_maxOrderLifetime 与 m_checkInterval 决定订单存活与轮询节奏,避免死单占坑。 公开构造器接收 symbol、volume、limitPrice、orderType,可见量默认在 0.0~0.0 间随机(useRandomVisibleVolume=true),avoidRoundNumbers 与 slippage=3 点均为出厂默认值,可直接在 MT5 里改参验证。 类还覆写 Initialize/Execute/Update/Terminate 四个虚函数,并暴露 GetRandomVisibleVolume、AdjustPriceToAvoidRoundNumbers、CheckAndReplaceOrder、IsOrderFilled 四个冰山专用方法——复制进 EA 框架后,重点看 CheckAndReplaceOrder 怎么在 m_maxOrderLifetime 超时后撤单重挂。

MQL5 / C++
  class="type">class="kw">double                m_maxVisibleVolume;       class=class="str">"cmt">// Maximum visible volume
  class="type">bool                  m_useRandomVisibleVolume; class=class="str">"cmt">// Whether to randomize visible volume
  class="type">int                   m_orderPlacementDelay;    class=class="str">"cmt">// Delay between order placements(ms)
  class="type">bool                  m_avoidRoundNumbers;      class=class="str">"cmt">// Whether to avoid round numbers in price
  class="type">class="kw">double                m_limitPrice;             class=class="str">"cmt">// Limit price for the orders
  class="type">ulong                 m_currentOrderTicket;     class=class="str">"cmt">// Current active order ticket
  ENUM_ORDER_TYPE       m_orderType;              class=class="str">"cmt">// Order type(buy or sell)
  class="type">bool                  m_orderActive;            class=class="str">"cmt">// Flag indicating if an order is currently active
  class="type">int                   m_priceDeviation;         class=class="str">"cmt">// Price deviation to avoid round numbers(in points)
  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="type">int                   m_maxOrderLifetime;       class=class="str">"cmt">// Maximum lifetime for an order in seconds
  class="type">class="kw">datetime              m_orderPlacementTime;     class=class="str">"cmt">// When the current order was placed

class="kw">public:
  class=class="str">"cmt">// Constructor
  CIcebergOrder(class="type">class="kw">string symbol, class="type">class="kw">double volume, class="type">class="kw">double limitPrice, ENUM_ORDER_TYPE orderType,
                class="type">class="kw">double visibleVolume, class="type">class="kw">double minVisibleVolume = class="num">0.0, class="type">class="kw">double maxVisibleVolume = class="num">0.0,
                class="type">bool useRandomVisibleVolume = true, class="type">int orderPlacementDelay = class="num">1000,
                class="type">bool avoidRoundNumbers = true, class="type">int priceDeviation = class="num">2, class="type">int slippage = class="num">3);

  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">// Iceberg specific methods
  class="type">class="kw">double               GetRandomVisibleVolume();
  class="type">class="kw">double               AdjustPriceToAvoidRoundNumbers(class="type">class="kw">double price);
  class="type">bool                 CheckAndReplaceOrder();
  class="type">bool                 IsOrderFilled(class="type">ulong ticket);

◍ 冰山单的挂单执行与状态自检

冰山算法下每一片碎单都不是盲目丢进市场的。Execute() 先判断 m_isActive,未激活直接返回 false 并打印提示,避免闲置状态下误触发挂单。 若 m_orderActive 已为真,说明上一片单子还挂着,这时不新开仓,而是走 CheckAndReplaceOrder() 去查状态、必要时替换,保证同一时刻只有一片可见挂单在跑。 可见手算有两种来源:开了随机可见量就取 GetRandomVisibleVolume(),否则用固定的 m_visibleVolume。代码里有一道硬约束——if(volumeToExecute > m_remainingVolume) 就截成剩余量,防止最后一片单超额。 下单走 MqlTradeRequest 直控:action 设 TRADE_ACTION_PENDING,magic 写死 123456 便于在 MT5 成交历史里筛冰山单。OrderSend 后分两层校验,success 为假看 GetLastError(),result.retcode 不等于 TRADE_RETCODE_DONE 也判失败。 过了校验才把 result.order 存进 m_currentOrderTicket,m_orderActive 置真,并用 TimeCurrent() 记挂单时间。外汇与贵金属杠杆高,碎单节奏失控可能放大滑点风险,参数请在模拟盘先验证。

MQL5 / C++
class="type">bool IsOrderPartiallyFilled(class="type">ulong ticket, class="type">class="kw">double &filledVolume);
class="type">bool IsOrderCancelled(class="type">ulong ticket);
class="type">bool IsOrderExpired(class="type">ulong ticket);
class="type">bool IsOrderTimeout();
class="type">ulong GetCurrentOrderTicket() { class="kw">return m_currentOrderTicket; }
class="type">bool IsOrderActive() { class="kw">return m_orderActive; }
};
class="type">bool CIcebergOrder::Execute()
{
   if(!m_isActive)
   {
      Print("Iceberg: Execute called but algorithm is not active");
      class="kw">return false;
   }
   
   class=class="str">"cmt">// If an order is already active, check its status
   if(m_orderActive)
   {
      Print("Iceberg: Execute called with active order ", m_currentOrderTicket);
      class="kw">return CheckAndReplaceOrder();
   }

   class=class="str">"cmt">// Calculate the volume for this execution
   class="type">class="kw">double volumeToExecute = m_useRandomVisibleVolume ?
                                 GetRandomVisibleVolume() :
                                 m_visibleVolume;
                                 
   class=class="str">"cmt">// Ensure we don&class="macro">#x27;t exceed the remaining volume
   if(volumeToExecute > m_remainingVolume)
      volumeToExecute = m_remainingVolume;
                                 
   Print("Iceberg: Placing order for ", DoubleToString(volumeToExecute, class="num">2),
         " at price ", DoubleToString(m_limitPrice, _Digits));
                                 
   class=class="str">"cmt">// Place the order using OrderSend directly for more control
   class="type">MqlTradeRequest request;
   class="type">MqlTradeResult result;
   ZeroMemory(request);
   ZeroMemory(result);

   request.action = TRADE_ACTION_PENDING;
   request.symbol = m_symbol;
   request.volume = volumeToExecute;
   request.type = m_orderType;
   request.price = m_limitPrice;
   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("Iceberg: OrderSend error: ", GetLastError());
      class="kw">return false;
   }

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

   class=class="str">"cmt">// Store the order ticket
   m_currentOrderTicket = result.order;
   m_orderActive = true;
   m_orderPlacementTime = TimeCurrent();

   Print("Iceberg: Order placed successfully. Ticket: ", m_currentOrderTicket,
         ", Volume: ", DoubleToString(volumeToExecute, class="num">2),

「冰山单的存活巡检与超时换单」

Update() 是冰山单算法在 MT5 里持续活着的核心。它先判断 m_isActive 是否为真,若算法已停止就直接返回 false 并打印提示,避免无意义的轮询消耗。 每次进入检查前会用 TimeCurrent() 对比 m_lastCheckTime + m_checkInterval,只有过了设定间隔才真正干活。这个间隔你在实盘里建议设在 500~2000 毫秒,间隔太短会被经纪商认定为高频行为,外汇与贵金属品种的高风险在于点差跳变可能让限价单长期不成交。 若 m_remainingVolume <= 0,说明碎单已全部吃完,调用 Terminate() 收尾。否则打印当前 Bid / Ask / 限价,用 SymbolInfoDouble 抓实时报价,DoubleToString 按 _Digits 精度输出,方便你直接在专家日志里核对挂单位置。 当 m_orderActive 为真且 IsOrderTimeout() 触发,算法会取消旧单、清空 ticket、Sleep(m_orderPlacementDelay) 后调 Execute() 重挂。没有活跃单时则直接 Execute() 开新碎单。把 m_orderPlacementDelay 调成 100~300 毫秒,能降低在同一价位被扫多次的概率。

MQL5 / C++
class="type">bool CIcebergOrder::Update()
{
  if(!m_isActive)
  {
    Print("Iceberg: Update called but algorithm is not active");
    class="kw">return false;
  }
    
  class=class="str">"cmt">// Check if all volume has been executed
  if(m_remainingVolume <= class="num">0)
  {
    Print("Iceberg: All volume executed. Terminating algorithm.");
    class="kw">return Terminate();
  }

  class=class="str">"cmt">// Check if it&class="macro">#x27;s time to check order status
  class="type">class="kw">datetime currentTime = TimeCurrent();
  if(currentTime >= m_lastCheckTime + m_checkInterval)
  {
    m_lastCheckTime = currentTime;
    
    class=class="str">"cmt">// Log current market conditions
    class="type">class="kw">double currentBid = SymbolInfoDouble(m_symbol, SYMBOL_BID);
    class="type">class="kw">double currentAsk = SymbolInfoDouble(m_symbol, SYMBOL_ASK);
    
    Print("Iceberg: Market update - Bid: ", DoubleToString(currentBid, _Digits),
          ", Ask: ", DoubleToString(currentAsk, _Digits),
          ", Limit Price: ", DoubleToString(m_limitPrice, _Digits));
    
    class=class="str">"cmt">// If an order is active, check its status
    if(m_orderActive)
    {
      class=class="str">"cmt">// Check if the order has been active too class="type">long
      if(IsOrderTimeout())
      {
        Print("Iceberg: Order ", m_currentOrderTicket, " has timed out. Replacing it.");
        
        class=class="str">"cmt">// Cancel the current order
        if(!CancelOrder(m_currentOrderTicket))
        {
          Print("Iceberg: Failed to cancel timed out order ", m_currentOrderTicket);
        }
        
        class=class="str">"cmt">// Reset order tracking
        m_orderActive = false;
        m_currentOrderTicket = class="num">0;
        
        class=class="str">"cmt">// Place a new order after a delay
        Sleep(m_orderPlacementDelay);
        class="kw">return Execute();
      }
      
      class="kw">return CheckAndReplaceOrder();
    }
    else
    {
      class=class="str">"cmt">// If no order is active, execute a new one
      Print("Iceberg: No active order, executing new order");
      class="kw">return Execute();
    }
  }
}

常见问题

用总时间除以间隔得到批次数,再用总目标手数除以批次数即得每批手数;实盘前先在模拟盘按此逻辑跑一遍确认无小数手数截断误差。
在巡检逻辑里加存活超时判断,若露出单超过设定时间未成交就撤单换价重挂,避免长期裸露暴露意图。
可以,小布能按你设定的超时阈值巡检挂单,露出单超时未成交时直接推送换单提醒,你只需确认是否执行。
CVWAP 需按实时成交量权重动态调每批手数,别直接套用固定间隔,否则在低波动段会堆单、高波动段吃不饱。
可能导致重复发单或误判已完成而停止后续批次;每次批处理后必须回写执行状态并做自检再进下一轮。