市场及其全局模式中的物理学·进阶篇
📘

市场及其全局模式中的物理学·进阶篇

第 2/3 篇

网格挂单的权重与重定价逻辑

这段私有方法负责在每根 K 线维度上重建 BuyStop / SellStop 的虚拟网格。核心参数 HalfCorridorLimitStop 由外部传入的 HalfCorridorLimitStop0 除以 OrdersToOneBar 得到,相当于把半通道宽度均摊到单根 bar 允许的挂单数量上。 CalcVolumeDecrease 用公式 K=1/(1-size) 与 C=1 做线性衰减:当网格层数 size>1 时,第 i 层权重 = TypeWeight*K*i + 1.0;size=1 则直接返回 0.0。CalcVolumeSimple 则无脑返回原始权重,两种模式由 WeightStopLimitFillingType 切换。 RebuildStops 先取 BuyStopOrders 数组长度 size,再从最新 bar 向历史回扫。每层挂单的 WantedPrice 以 Open[j+1] 为基准,加减 HalfCorridorLimitStop*(i+1)*Point 形成等差网格;同时写入 ProfitPointsCorridorPart 与 LossPointsCorridorPart 算出的上下平仓边界,以及 ExpirationOpenStop / ExpirationClose 过期柱数。 开 MT5 把 WeightStopLimitFillingType 改成 WEIGHT_DECREASE,观察 size=5 时第 4 层权重相对首层放大约 4 倍(K 为负值导致远端权重更低),外汇与贵金属杠杆品种下这种衰减可能放大滑点风险,建议先用策略测试器跑历史 tick 验证。

MQL5 / C++
HalfCorridorLimitStop=class="type">class="kw">double(HalfCorridorLimitStop0)/class="type">class="kw">double(OrdersToOneBar);
ExpirationOpenLimit=ExpirationOpenLimit0;
ExpirationOpenStop=ExpirationOpenStop0;
ExpirationClose=ExpirationClose0;
ProfitPointsCorridorPart=ProfitPointsCorridorPart0;
LossPointsCorridorPart=LossPointsCorridorPart0;
OrdersToOneBar=OrdersToOneBar0;
WeightStopLimitFillingType=WeightStopLimitFillingType0;
}
  class="kw">private:

class="type">class="kw">double CalcVolumeDecrease(class="type">class="kw">double TypeWeight,class="type">int i,class="type">int size)class=class="str">"cmt">// attenuation volume
  {
  if ( size > class="num">1 )
   {
   class="type">class="kw">double K=class="num">1.0/(class="num">1.0-size);
   class="type">class="kw">double C=class="num">1.0;
   class="kw">return TypeWeight*K*i+C;
   }
  else class="kw">return class="num">0.0;
  }

  class="type">class="kw">double CalcVolumeSimple(class="type">class="kw">double TypeWeight)class=class="str">"cmt">// equal volume
  {
  class="kw">return TypeWeight;
  }

  class="type">void RebuildStops()class=class="str">"cmt">// rebuild stop orders
  {
  class="type">int size=ArraySize(BarOrders[class="num">0].BuyStopOrders);
  for ( class="type">int j=ArraySize(BarOrders)-class="num">1; j>=class="num">0; j-- )
   {
   for ( class="type">int i=class="num">0; i<size; i++ )class=class="str">"cmt">// reset all
    {
    BarOrders[j].BuyStopOrders[i].Status=STATUS_VIRTUAL;class=class="str">"cmt">// reset status to initial
    BarOrders[j].BuyStopOrders[i].WantedPrice=Open[j+class="num">1]+HalfCorridorLimitStop*(i+class="num">1)*Point;class=class="str">"cmt">// prices of the order grid
    if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[j].BuyStopOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaStop,i,size);class=class="str">"cmt">// weight of each element of the grid
    if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[j].BuyStopOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaStop);class=class="str">"cmt">// current weight of each element of the grid
    BarOrders[j].BuyStopOrders[i].VolumeStart=BarOrders[j].BuyStopOrders[i].VolumeAlpha;class=class="str">"cmt">// starting weight of each element of the grid
    BarOrders[j].BuyStopOrders[i].UpPriceToClose=BarOrders[j].BuyStopOrders[i].WantedPrice+ProfitPointsCorridorPart*Point;class=class="str">"cmt">// upper border to close
    BarOrders[j].BuyStopOrders[i].LowPriceToClose=BarOrders[j].BuyStopOrders[i].WantedPrice-LossPointsCorridorPart*Point;class=class="str">"cmt">// lower border to close
    BarOrders[j].BuyStopOrders[i].BarsExpirationOpen=ExpirationOpenStop;
    BarOrders[j].BuyStopOrders[i].BarsExpirationClose=ExpirationClose;

    BarOrders[j].SellStopOrders[i].Status=STATUS_VIRTUAL;
    BarOrders[j].SellStopOrders[i].WantedPrice=Open[j+class="num">1]-HalfCorridorLimitStop*(i+class="num">1)*Point;class=class="str">"cmt">// prices of the order grid

「网格挂单的权重与边界重建逻辑」

在虚拟挂单网格里,每一根 K 线都对应一组 Buy/Sell 的 Stop 与 Limit 订单。重建时先按 WeightStopLimitFillingType 决定手数分配:递减模式调 CalcVolumeDecrease,等权模式调 CalcVolumeSimple,两者直接决定单格 VolumeAlpha 的初值。 以 BuyLimit 为例,挂单价取 Open[j+1] 减去 HalfCorridorLimitStop*(i+1)*Point,也就是沿中枢向下等距铺网格;UpPriceToClose 与 LowPriceToClose 则分别用 ProfitPointsCorridorPart 和 LossPointsCorridorPart 乘 Point 偏移出来,构成每格的平仓通道。外汇与贵金属波动剧烈,这类通道若参数过窄,可能在几根 bar 内被双向扫损,实盘前务必在 MT5 策略测试器里用历史数据回放验证。 SellLimit 的算法对称:WantedPrice = Open[j+1] + HalfCorridorLimitStop*(i+1)*Point,向上铺网格。RebuildLimits 函数开头会把所有订单 Status 重置为 STATUS_VIRTUAL,再统一写入价格、权重、到期 bar 数,保证上一轮残留状态不污染新网格。

MQL5 / C++
if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[j].SellStopOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaStop,i,size);class=class="str">"cmt">// weight of each element of the grid
if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[j].SellStopOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaStop);class=class="str">"cmt">// current weight of each element of the grid
BarOrders[j].SellStopOrders[i].VolumeStart=BarOrders[j].SellStopOrders[i].VolumeAlpha;class=class="str">"cmt">// starting weight of each element of the grid
BarOrders[j].SellStopOrders[i].UpPriceToClose=BarOrders[j].SellStopOrders[i].WantedPrice+LossPointsCorridorPart*Point;class=class="str">"cmt">// upper border to close
BarOrders[j].SellStopOrders[i].LowPriceToClose=BarOrders[j].SellStopOrders[i].WantedPrice-ProfitPointsCorridorPart*Point;class=class="str">"cmt">// lower border to close
BarOrders[j].SellStopOrders[i].BarsExpirationOpen=ExpirationOpenStop;
BarOrders[j].SellStopOrders[i].BarsExpirationClose=ExpirationClose;
}
}

class="type">void RebuildLimits()class=class="str">"cmt">// rebuild limit orders
{
class="type">int size=ArraySize(BarOrders[class="num">0].BuyLimitOrders);
for ( class="type">int j=ArraySize(BarOrders)-class="num">1; j>=class="num">0; j-- )
  {
   for ( class="type">int i=class="num">0; i<size; i++ )class=class="str">"cmt">// reset all
    {
    BarOrders[j].BuyLimitOrders[i].Status=STATUS_VIRTUAL;class=class="str">"cmt">// reset status to initial
    BarOrders[j].BuyLimitOrders[i].WantedPrice=Open[j+class="num">1]-HalfCorridorLimitStop*(i+class="num">1)*Point;class=class="str">"cmt">// prices of the order grid
    if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[j].BuyLimitOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaLimit,i,size);class=class="str">"cmt">// weight of each element of the grid
    if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[j].BuyLimitOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaLimit);class=class="str">"cmt">// current weight of each element of the grid
    BarOrders[j].BuyLimitOrders[i].VolumeStart=BarOrders[j].BuyLimitOrders[i].VolumeAlpha;class=class="str">"cmt">// starting weight of each element of the grid
    BarOrders[j].BuyLimitOrders[i].UpPriceToClose=BarOrders[j].BuyLimitOrders[i].WantedPrice+ProfitPointsCorridorPart*Point;class=class="str">"cmt">// upper border to close
    BarOrders[j].BuyLimitOrders[i].LowPriceToClose=BarOrders[j].BuyLimitOrders[i].WantedPrice-LossPointsCorridorPart*Point;class=class="str">"cmt">// lower border to close
    BarOrders[j].BuyLimitOrders[i].BarsExpirationOpen=ExpirationOpenLimit;
    BarOrders[j].BuyLimitOrders[i].BarsExpirationClose=ExpirationClose;

    BarOrders[j].SellLimitOrders[i].Status=STATUS_VIRTUAL;
    BarOrders[j].SellLimitOrders[i].WantedPrice=Open[j+class="num">1]+HalfCorridorLimitStop*(i+class="num">1)*Point;class=class="str">"cmt">// prices of the order grid
    if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[j].SellLimitOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaLimit,i,size);class=class="str">"cmt">// weight of each element of the grid

◍ 网格挂单与市价单的权重及边界初始化

这段逻辑负责在每根 K 线重建时,把限价单和市价单网格的权重、挂单价格、平仓通道上下沿一次性写好。外汇与贵金属杠杆高,网格参数若直接照搬容易在跳空时放大回撤,建议先在 MT5 策略测试器用历史数据跑一遍再上实盘。 限价卖单部分,当权重模式为 WEIGHT_SAME 时,每个网格元素的当前权重由 CalcVolumeSimple(VolumeAlphaLimit) 算出,起始权重与之相等;平仓上沿 = 期望价 + LossPointsCorridorPart*Point,下沿 = 期望价 - ProfitPointsCorridorPart*Point,并写入开单与平仓的 K 线过期数。 RebuildMarkets() 里先取 BuyMarketOrders 数组大小,用前一根 K 线振幅除以 OrdersToOneBar 得到 MarketStep。双向市价单都重置为 STATUS_MARKET:买网格价从 Low[j+1] 起按 i*MarketStep 递推,卖网格价从 High[j+1] 起向下递减;平仓通道对买单是上盈利下止损,对卖单正好反向。 下沿边界参数 ProfitPointsCorridorPart 与 LossPointsCorridorPart 直接决定通道宽度,调小它们会让网格更快平仓但胜率可能下降,这是验证时第一个该动的旋钮。

MQL5 / C++
if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[j].SellLimitOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaLimit);class=class="str">"cmt">// current weight of each element of the grid
BarOrders[j].SellLimitOrders[i].VolumeStart=BarOrders[j].SellLimitOrders[i].VolumeAlpha;class=class="str">"cmt">// starting weight of each element of the grid
BarOrders[j].SellLimitOrders[i].UpPriceToClose=BarOrders[j].SellLimitOrders[i].WantedPrice+LossPointsCorridorPart*Point;class=class="str">"cmt">// upper border to close
BarOrders[j].SellLimitOrders[i].LowPriceToClose=BarOrders[j].SellLimitOrders[i].WantedPrice-ProfitPointsCorridorPart*Point;class=class="str">"cmt">// lower border to close
BarOrders[j].SellLimitOrders[i].BarsExpirationOpen=ExpirationOpenLimit;
BarOrders[j].SellLimitOrders[i].BarsExpirationClose=ExpirationClose;
}
}
}

class="type">void RebuildMarkets()class=class="str">"cmt">// rebuild market orders
{
class="type">int size=ArraySize(BarOrders[class="num">0].BuyMarketOrders);
class="type">class="kw">double MarketStep;
for ( class="type">int j=ArraySize(BarOrders)-class="num">1; j>class="num">0; j-- )
  {
  MarketStep=(High[j+class="num">1]-Low[j+class="num">1])/class="type">class="kw">double(OrdersToOneBar);
  for ( class="type">int i=class="num">0; i<size; i++ )class=class="str">"cmt">// reset all
    {
    BarOrders[j].BuyMarketOrders[i].Status=STATUS_MARKET;class=class="str">"cmt">// reset status to initial
    BarOrders[j].BuyMarketOrders[i].WantedPrice=Low[j+class="num">1]+MarketStep*i;class=class="str">"cmt">// prices of the order grid
    BarOrders[j].BuyMarketOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaMarket);class=class="str">"cmt">// current weight of each element of the grid
    BarOrders[j].BuyMarketOrders[i].VolumeStart=BarOrders[j].BuyMarketOrders[i].VolumeAlpha;class=class="str">"cmt">// starting weight of each element of the grid
    BarOrders[j].BuyMarketOrders[i].UpPriceToClose=BarOrders[j].BuyMarketOrders[i].WantedPrice+ProfitPointsCorridorPart*Point;class=class="str">"cmt">// upper border to close
    BarOrders[j].BuyMarketOrders[i].LowPriceToClose=BarOrders[j].BuyMarketOrders[i].WantedPrice-LossPointsCorridorPart*Point;class=class="str">"cmt">// lower border to close
    BarOrders[j].BuyMarketOrders[i].BarsExpirationClose=ExpirationClose;
    
    BarOrders[j].SellMarketOrders[i].Status=STATUS_MARKET;
    BarOrders[j].SellMarketOrders[i].WantedPrice=High[j+class="num">1]-MarketStep*i;class=class="str">"cmt">// prices of the order grid
    BarOrders[j].SellMarketOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaMarket);class=class="str">"cmt">// current weight of each element of the grid
    BarOrders[j].SellMarketOrders[i].VolumeStart=BarOrders[j].SellMarketOrders[i].VolumeAlpha;class=class="str">"cmt">// starting weight of each element of the grid
    BarOrders[j].SellMarketOrders[i].UpPriceToClose=BarOrders[j].SellMarketOrders[i].WantedPrice+LossPointsCorridorPart*Point;class=class="str">"cmt">// upper border to close
    BarOrders[j].SellMarketOrders[i].LowPriceToClose=BarOrders[j].SellMarketOrders[i].WantedPrice-ProfitPointsCorridorPart*Point;class=class="str">"cmt">// lower border to close

止损挂单网格的跨Bar重建逻辑

在 MT5 的 EA 结构里,止损挂单(BuyStop / SellStop)通常不是一次性铺好就不动,而是跟着每根新 K 线滚动重建。RebuildStopsFast 这个函数干的就是这件事:把上一根 Bar 的网格整体后移一格,再在最新 Bar 上重算一套全新虚拟挂单。 外层循环从倒数第二根 Bar 往前推,内层把 BuyStopOrders 和 SellStopOrders 数组整体向后平移,同时给 SellStopOrders 的 IndexMarket 自增 1,用来标记该挂单在当前市场结构里所处的相对位置。 最新 Bar(索引 0)的网格则完全重算:WantedPrice 以 Close[1] 为基准,加减 HalfCorridorLimitStop*(i+1)*Point 形成等距走廊;VolumeAlpha 按 WEIGHT_DECREASE 或 WEIGHT_SAME 两种权重模式分别调用 CalcVolumeDecrease / CalcVolumeSimple 得出,再写入 VolumeStart 作为初始权重。 每笔挂单还绑定了上下平仓边界:UpPriceToClose 与 LowPriceToClose 分别落在 WantedPrice 加减 ProfitPointsCorridorPart / LossPointsCorridorPart 个 Point 的位置,并写入 BarsExpirationOpen 与 BarsExpirationClose 控制挂单存活与平仓时效。外汇与贵金属波动剧烈,这类网格在跳空时可能多单同时触发,实盘前务必在策略测试器里用历史数据验证走廊宽度与权重参数的敏感性。

MQL5 / C++
class=class="str">"cmt">///// Fast methods
class="type">void RebuildStopsFast()class=class="str">"cmt">// rebuild stop orders
  {
   class="type">int size=ArraySize(BarOrders[class="num">0].BuyStopOrders);
   for ( class="type">int j=ArraySize(BarOrders)-class="num">1; j>class="num">0; j-- )
     {
      for ( class="type">int i=class="num">0; i<size; i++ )class=class="str">"cmt">// shift orders
        {
         BarOrders[j].BuyStopOrders[i]=BarOrders[j-class="num">1].BuyStopOrders[i];
         BarOrders[j].SellStopOrders[i]=BarOrders[j-class="num">1].SellStopOrders[i];
         BarOrders[j].SellStopOrders[i].IndexMarket++;
        }
     }
     
   for ( class="type">int i=class="num">0; i<size; i++ )class=class="str">"cmt">// create a new grid at a new bar
     {
      BarOrders[class="num">0].BuyStopOrders[i].Status=STATUS_VIRTUAL;class=class="str">"cmt">// reset status to initial
      BarOrders[class="num">0].BuyStopOrders[i].WantedPrice=Close[class="num">1]+HalfCorridorLimitStop*(i+class="num">1)*Point;class=class="str">"cmt">// prices of the order grid
      if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[class="num">0].BuyStopOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaStop,i,size);class=class="str">"cmt">// weight of each element of the grid
      if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[class="num">0].BuyStopOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaStop);class=class="str">"cmt">// current weight of each element of the grid
      BarOrders[class="num">0].BuyStopOrders[i].VolumeStart=BarOrders[class="num">0].BuyStopOrders[i].VolumeAlpha;class=class="str">"cmt">// starting weight of each element of the grid
      BarOrders[class="num">0].BuyStopOrders[i].UpPriceToClose=BarOrders[class="num">0].BuyStopOrders[i].WantedPrice+ProfitPointsCorridorPart*Point;class=class="str">"cmt">//upper border to close
      BarOrders[class="num">0].BuyStopOrders[i].LowPriceToClose=BarOrders[class="num">0].BuyStopOrders[i].WantedPrice-LossPointsCorridorPart*Point;class=class="str">"cmt">// lower border to close
      BarOrders[class="num">0].BuyStopOrders[i].BarsExpirationOpen=ExpirationOpenStop;
      BarOrders[class="num">0].BuyStopOrders[i].BarsExpirationClose=ExpirationClose;
      
      BarOrders[class="num">0].SellStopOrders[i].Status=STATUS_VIRTUAL;
      BarOrders[class="num">0].SellStopOrders[i].WantedPrice=Close[class="num">1]-HalfCorridorLimitStop*(i+class="num">1)*Point;class=class="str">"cmt">// prices of the order grid
      if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[class="num">0].SellStopOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaStop,i,size);class=class="str">"cmt">// weight of each element of the grid
      if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[class="num">0].SellStopOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaStop);class=class="str">"cmt">// current weight of each element of the grid
      BarOrders[class="num">0].SellStopOrders[i].VolumeStart=BarOrders[class="num">0].SellStopOrders[i].VolumeAlpha;class=class="str">"cmt">// starting weight of each element of the grid

「限价挂单网格的平移与重建逻辑」

在 MT5 的 EA 结构中,RebuildLimitsFast 负责把旧 K 线柱上的 BuyLimit / SellLimit 网格整体后移,并在最新柱上重铺一层。外层循环从 ArraySize(BarOrders)-1 递减到 1,把 j-1 的挂单对象直接赋值给 j,同时 SellLimit 的 IndexMarket 自增——这意味着越老的柱,其挂单在市场序列里的索引越靠后。 新柱的网格以 Open[1] 为基准向两侧铺开:BuyLimit 取 Open[1] 减去 HalfCorridorLimitStop*(i+1)*Point,SellLimit 则加上同样距离。每个格子的 VolumeAlpha 按 WEIGHT_DECREASE 或 WEIGHT_SAME 两种模式计算,前者调用 CalcVolumeDecrease 递减权重,后者用 CalcVolumeSimple 给统一权重,随后写入 VolumeStart 作为起始手数系数。 平仓边界由 ProfitPointsCorridorPart 与 LossPointsCorridorPart 乘 Point 决定,分别挂在 WantedPrice 的上下侧;BarsExpirationOpen 和 BarsExpirationClose 则接管挂单生效与平仓失效的柱数。外汇与贵金属杠杆高,这类网格在跳空时可能连续触发,实盘前务必用策略测试器按不同点差校验 ExpirationClose 的合理性。 直接把这段结构拷进你的 include 文件,改 HalfCorridorLimitStop 从 50 到 200 点,观察 15 分钟图 EURUSD 上虚拟挂单密度变化,能快速判断参数是否过密。

MQL5 / C++
class="type">void RebuildLimitsFast()class=class="str">"cmt">// rebuild limit orders
  {
  class="type">int size=ArraySize(BarOrders[class="num">0].BuyLimitOrders);
  for ( class="type">int j=ArraySize(BarOrders)-class="num">1; j>class="num">0; j-- )
    {
    for ( class="type">int i=class="num">0; i<size; i++ )class=class="str">"cmt">// shift orders
      {
      BarOrders[j].BuyLimitOrders[i]=BarOrders[j-class="num">1].BuyLimitOrders[i];
      BarOrders[j].SellLimitOrders[i]=BarOrders[j-class="num">1].SellLimitOrders[i];
      BarOrders[j].SellLimitOrders[i].IndexMarket++;
      }
    }
  
  for ( class="type">int i=class="num">0; i<size; i++ )class=class="str">"cmt">// create a new grid at a new bar
    {
    BarOrders[class="num">0].BuyLimitOrders[i].Status=STATUS_VIRTUAL;class=class="str">"cmt">// reset status to initial
    BarOrders[class="num">0].BuyLimitOrders[i].WantedPrice=Open[class="num">1]-HalfCorridorLimitStop*(i+class="num">1)*Point;class=class="str">"cmt">// prices of the order grid
    if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[class="num">0].BuyLimitOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaLimit,i,size);class=class="str">"cmt">// weight of each element of the grid
    if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[class="num">0].BuyLimitOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaLimit);class=class="str">"cmt">// current weight of each element of the grid
    BarOrders[class="num">0].BuyLimitOrders[i].VolumeStart=BarOrders[class="num">0].BuyLimitOrders[i].VolumeAlpha;class=class="str">"cmt">// starting weight of each element of the grid
    BarOrders[class="num">0].BuyLimitOrders[i].UpPriceToClose=BarOrders[class="num">0].BuyLimitOrders[i].WantedPrice+ProfitPointsCorridorPart*Point;class=class="str">"cmt">// upper border to close
    BarOrders[class="num">0].BuyLimitOrders[i].LowPriceToClose=BarOrders[class="num">0].BuyLimitOrders[i].WantedPrice-LossPointsCorridorPart*Point;class=class="str">"cmt">// lower border to close
    BarOrders[class="num">0].BuyLimitOrders[i].BarsExpirationOpen=ExpirationOpenLimit;
    BarOrders[class="num">0].BuyLimitOrders[i].BarsExpirationClose=ExpirationClose;
    
    BarOrders[class="num">0].SellLimitOrders[i].Status=STATUS_VIRTUAL;
    BarOrders[class="num">0].SellLimitOrders[i].WantedPrice=Open[class="num">1]+HalfCorridorLimitStop*(i+class="num">1)*Point;class=class="str">"cmt">// prices of the order grid

◍ 市价单网格在新K线上的重建逻辑

RebuildMarketsFast() 负责把上一根 K 线的市价单网格整体后移,并在最新 Bar 上按区间重新铺网格。外层循环从 ArraySize(BarOrders)-1 递减到 1,把 j-1 的 Buy/Sell 市价单直接赋值给 j,实现订单结构的滚动位移。 新网格的步进用 MarketStep=(High[1]-Low[1])/double(OrdersToOneBar) 计算,也就是以上一根 K 线真实波幅均分成 OrdersToOneBar 份。BuyMarketOrders[i].WantedPrice 取 Low[1]+MarketStep*i,挂单价格随序号线性铺开。 每格初始权重由 CalcVolumeSimple(VolumeAlphaMarket) 给出,VolumeStart 与 VolumeAlpha 同步;平仓通道上下沿分别用 WantedPrice 加减 Profit/LossPointsCorridorPart*Point 确定。外汇与贵金属杠杆高,这类网格在跳空时可能整层不成交,上 MT5 用指数品种回测前先把 ExpirationClose 和 OrdersToOneBar 两个参数试一遍。

MQL5 / C++
class="type">void RebuildMarketsFast()class=class="str">"cmt">// rebuild market orders
  {
  class="type">int size=ArraySize(BarOrders[class="num">0].BuyMarketOrders);
  class="type">class="kw">double MarketStep;
  for ( class="type">int j=ArraySize(BarOrders)-class="num">1; j>class="num">0; j-- )
    {
    for ( class="type">int i=class="num">0; i<size; i++ )class=class="str">"cmt">// shift orders
      {
      BarOrders[j].BuyMarketOrders[i]=BarOrders[j-class="num">1].BuyMarketOrders[i];
      BarOrders[j].SellMarketOrders[i]=BarOrders[j-class="num">1].SellMarketOrders[i];
      }
    }
  MarketStep=(High[class="num">1]-Low[class="num">1])/class="type">class="kw">double(OrdersToOneBar);
  for ( class="type">int i=class="num">0; i<size; i++ )class=class="str">"cmt">// create a new grid at a new bar
    {
    BarOrders[class="num">0].BuyMarketOrders[i].Status=STATUS_MARKET;class=class="str">"cmt">// reset status to initial
    BarOrders[class="num">0].BuyMarketOrders[i].WantedPrice=Low[class="num">1]+MarketStep*i;class=class="str">"cmt">// prices of the order grid
    BarOrders[class="num">0].BuyMarketOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaMarket);class=class="str">"cmt">// current weight of each element of the grid
    BarOrders[class="num">0].BuyMarketOrders[i].VolumeStart=BarOrders[class="num">0].BuyMarketOrders[i].VolumeAlpha;class=class="str">"cmt">// starting weight of each element of the grid
    BarOrders[class="num">0].BuyMarketOrders[i].UpPriceToClose=BarOrders[class="num">0].BuyMarketOrders[i].WantedPrice+ProfitPointsCorridorPart*Point;class=class="str">"cmt">// upper border to close
    BarOrders[class="num">0].BuyMarketOrders[i].LowPriceToClose=BarOrders[class="num">0].BuyMarketOrders[i].WantedPrice-LossPointsCorridorPart*Point;class=class="str">"cmt">// lower border to close
    BarOrders[class="num">0].BuyMarketOrders[i].BarsExpirationClose=ExpirationClose;

    BarOrders[class="num">0].SellMarketOrders[i].Status=STATUS_MARKET;

常见问题

当报价越过网格边界,系统按预设步长平移整组挂单并重置权重,使新边界覆盖当前价,避免漏单。
不会。跨Bar时按收盘位置重建网格边界,原有权重比例沿用,仅对外缘空白区做补单。
小布会实时比对各品种网格边界与报价距离,异常平移或权重归零时直接弹提醒,省去手动翻单。
可以。价格偏离超阈值后网格按步长整体平移并重算权重,原未成交单撤掉重挂到新区间。
需要。每根新K线按开盘价重设基准并重建网格权重,保证统计样本与当前波动匹配。