市场及其全局模式中的物理学·综合运用
📐

市场及其全局模式中的物理学·综合运用

(3/3)·把前两层概念与实战拼图合拢,看数学语言如何落地成可测的专家顾问

案例拆解 第 3/3 篇
把价格当成纯随机游走的人,常忽略它的一阶导数里藏着可解析成分。用无限精细的连续公式去套离散报价,只会得到无法交易的伪信号。先承认不可预测项的存在,才谈得上让数学替你干活。

「卖单网格的字段装配与刷新入口」

这段代码在每根 K 线出现时,给第 0 根 Bar 的卖单网格数组逐项填值。WantedPrice 用前一根 High 减去 MarketStep*i,意味着网格挂单价随序号线性下移,步长由 MarketStep 控制;VolumeAlpha 和 VolumeStart 都来自 CalcVolumeSimple(VolumeAlphaMarket),即网格每个元素的初始权重一致。 UpPriceToClose 与 LowPriceToClose 分别等于挂单价加减走廊点数(LossPointsCorridorPart 与 ProfitPointsCorridorPart 乘 Point),构成该网格单的平仓区间边界。BarsExpirationClose 直接取 ExpirationClose,决定超时强平的根数。 PositionGenerator 里用 CreateNewOrders() 和 CreateNewOrdersFast() 分流:只要对应 VolumeAlpha 不为 0.0,就调用 Rebuild 系列函数重建 Stop / Limit / Market 挂单。Update() 与 UpdateFast() 是两个虚函数,子类可重写,但基类默认都只调上面的创建函数。 Simulation 类继承自 PositionGenerator,额外维护 BuyPercent、SellPercent、StartVolume 等市场统计字段,后续行情百分比与总持仓量都从这里算。开 MT5 把 MarketStep 从 10 改到 20 点,能直接看到网格密度变稀一半。

MQL5 / C++
BarOrders[class="num">0].SellMarketOrders[i].WantedPrice=High[class="num">1]-MarketStep*i;class=class="str">"cmt">// prices of the order grid
BarOrders[class="num">0].SellMarketOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaMarket);class=class="str">"cmt">// current weight of each element of the grid
BarOrders[class="num">0].SellMarketOrders[i].VolumeStart=BarOrders[class="num">0].SellMarketOrders[i].VolumeAlpha;class=class="str">"cmt">// starting weight of each element of the grid
BarOrders[class="num">0].SellMarketOrders[i].UpPriceToClose=BarOrders[class="num">0].SellMarketOrders[i].WantedPrice+LossPointsCorridorPart*Point;class=class="str">"cmt">// upper border to close
BarOrders[class="num">0].SellMarketOrders[i].LowPriceToClose=BarOrders[class="num">0].SellMarketOrders[i].WantedPrice-ProfitPointsCorridorPart*Point;class=class="str">"cmt">// lower border to close
BarOrders[class="num">0].SellMarketOrders[i].BarsExpirationClose=ExpirationClose;
}
}

class="kw">protected:
class="type">void CreateNewOrders()class=class="str">"cmt">// create new orders at each candlestick
{
if ( VolumeAlphaStop != class="num">0.0 ) RebuildStops();
if ( VolumeAlphaLimit != class="num">0.0 ) RebuildLimits();
if ( VolumeAlphaMarket != class="num">0.0 ) RebuildMarkets();
}

class="type">void CreateNewOrdersFast()class=class="str">"cmt">//
{
if ( VolumeAlphaStop != class="num">0.0 ) RebuildStopsFast();
if ( VolumeAlphaLimit != class="num">0.0 ) RebuildLimitsFast();
if ( VolumeAlphaMarket != class="num">0.0 ) RebuildMarketsFast();
}

class="kw">public:
class="kw">virtual class="type">void Update()class=class="str">"cmt">// state updating function(will be expanded in child classes)
{
CreateNewOrders();
}

class="kw">virtual class="type">void UpdateFast()class=class="str">"cmt">// fast state update
{
CreateNewOrdersFast();
}
};
class Simulation:class="kw">public PositionGenerator class=class="str">"cmt">// then assemble a simulator of positions(inherited from the position generator)
{
class="kw">protected:
class="type">class="kw">double BuyPercent;class=class="str">"cmt">// percent of open Buy positions
class="type">class="kw">double SellPercent;class=class="str">"cmt">// percent of open Sell positions
class="type">class="kw">double StartVolume;class=class="str">"cmt">// starting total volume of open Buy positions(the same for Buys and Sells)
class="type">class="kw">double RelativeVolume;class=class="str">"cmt">// relative volume
class="type">class="kw">double SummVolumeBuy;class=class="str">"cmt">// total volume for Buys
class="type">class="kw">double SummVolumeSell;class=class="str">"cmt">// total volume for Sells

class="kw">public:
Simulation( ENUM_GRID_WEIGHT WeightStopLimitFillingType0
, class="type">int HalfCorridorLimitStop0, class="type">int OrdersToOneBar0, class="type">int BarsTotal0
, class="type">int ExpirationOpenLimit0, class="type">int ExpirationOpenStop0
, class="type">int ExpirationClose0
, class="type">int ProfitPointsCorridorPart0, class="type">int LossPointsCorridorPart0

◍ 仓位生成器的初始化与实时刷新链路

这段 C++ 风格的 MQL5 类构造,把挂单与市价单的刷新拆成了两条明确路径。构造函数里先调 CreateNewOrders 建仓框架,再依次跑 CalculateStartVolume、UpdateVirtual、UpdateMarket,把虚拟单先更新一遍,下一函数才能把其中一部分当市价单处理。 公开接口 GetBuyPercent、GetSellPercent、GetRelativeVolume 直接回传内部成员变量,说明多空占比和相对成交量是在每轮 Update 里算好的,调用方拿的是现成值,不是临时聚合。 Update 与 UpdateFast 的区别在于后者用 *_Fast 版子函数(如 UpdateVirtualFast),跳过部分重算以提速;两者都先调基类 PositionGenerator::Update 保底逻辑。下面这段代码是虚拟单刷新的入口,注意它先取 BuyLimitOrders 数组长度,再取 BarOrders 总棒数: [CODE] void UpdateVirtual() // update the status of virtual orders { int size=ArraySize(BarOrders[0].BuyLimitOrders); int SizeBarOrders=ArraySize(BarOrders); if ( VolumeAlphaLimit != 0.0 ) { [CODE] 在 MT5 里把 VolumeAlphaLimit 设成 0.0 会直接跳过整段虚拟单更新,回测时若发现 Limit 单不动,先查这个开关而不是去怀疑点差。外汇与贵金属杠杆高,这类内部状态错乱可能让仓位暴露在不预期的风险中。

MQL5 / C++
class="type">void UpdateVirtual() class=class="str">"cmt">// update the status of class="kw">virtual orders
  {
   class="type">int size=ArraySize(BarOrders[class="num">0].BuyLimitOrders);
   class="type">int SizeBarOrders=ArraySize(BarOrders);

   if ( VolumeAlphaLimit != class="num">0.0 )
     {

虚拟挂单怎么被K线吃掉

回测里模拟限价单,核心就是拿每根K线的高低价去套前面的虚拟挂单。下面这段三层循环干的就是这件事:外层 i 从最新K线倒退,中层 j 遍历 i 之前的已有K线,内层 k 扫该K线内的多空挂单数组。 当某笔 BuyLimit 还是 STATUS_VIRTUAL,且它的 WantedPrice 落在当前 K 线 [Low[i], High[i]] 区间内,就判定被触发,状态改成 STATUS_MARKET 并记录触发索引 i。SellLimit 同理,不区分方向只认价格穿透。 挂单还有兴趣衰减机制:若一直没触发,且距挂出已过的K线数(IndexMarket-1)大于等于 BarsExpirationOpen,直接标 STATUS_ABORTED。否则每根K线按 VolumeStart/BarsExpirationOpen 扣减 VolumeAlpha,下限截到 0.0,模拟限价者耐心流失。外汇与贵金属波动大,这种模拟对滑点和点差零容错,实盘前务必在 MT5 策略测试器用历史数据跑一遍验证。

MQL5 / C++
for ( class="type">int i=SizeBarOrders; i>class="num">0; i-- )class=class="str">"cmt">// update the state of limit orders simulating each candlestick
      {
      for ( class="type">int j=SizeBarOrders-class="num">1; j>i; j-- )class=class="str">"cmt">// update the state of all candlesticks preceding this one
         {
         for ( class="type">int k=class="num">0; k<size; k++ )class=class="str">"cmt">// update the state inside each preceding candlestick
            {
            if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_VIRTUAL
            && BarOrders[j].BuyLimitOrders[k].WantedPrice <= High[i]
            && BarOrders[j].BuyLimitOrders[k].WantedPrice >= Low[i] )class=class="str">"cmt">// if the order is class="kw">virtual and is inside a candlestick, then it turns into a market one
               {
               BarOrders[j].BuyLimitOrders[k].Status = STATUS_MARKET;
               BarOrders[j].BuyLimitOrders[k].IndexMarket = i;
               }
            if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_VIRTUAL
            && BarOrders[j].SellLimitOrders[k].WantedPrice <= High[i]
            && BarOrders[j].SellLimitOrders[k].WantedPrice >= Low[i] )class=class="str">"cmt">// the same
               {
               BarOrders[j].SellLimitOrders[k].Status = STATUS_MARKET;
               BarOrders[j].SellLimitOrders[k].IndexMarket = i;
               }

            class=class="str">"cmt">/////// Check for interest expiration of limit players
            if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_VIRTUAL )class=class="str">"cmt">//
               {
               if ( BarOrders[j].BuyLimitOrders[k].IndexMarket - class="num">1 >= BarOrders[j].BuyLimitOrders[k].BarsExpirationOpen )
               BarOrders[j].BuyLimitOrders[k].Status=STATUS_ABORTED;
               else
                 {
                 if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha > class="num">0.0 )
                 BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart/class="type">class="kw">double(BarOrders[j].BuyLimitOrders[k].BarsExpirationOpen);
                 if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha < class="num">0.0 ) BarOrders[j].BuyLimitOrders[k].VolumeAlpha=class="num">0.0;
                 }
            }

「挂单状态在逐根K线上的回滚逻辑」

这段逻辑处理的是虚拟挂单在每根蜡烛上的生命周期推演。以 SellLimit 为例,若挂单仍处于 STATUS_VIRTUAL,先判断当前市价索引距开盘已过的 BarsExpirationOpen 根数:一旦 IndexMarket-1 大于等于设定的存续根数,订单直接标记为 STATUS_ABORTED 作废。 若未过期,则对 VolumeAlpha 做线性衰减——每根 K 线扣减 VolumeStart 除以 BarsExpirationOpen 的量。当衰减结果跌破 0.0 时强制钳制为 0.0,避免负仓位系数参与后续计算。 下方 VolumeAlphaStop 非零时进入第二层循环:逆序遍历 SizeBarOrders,对每根后续 K 线 i 检查前置蜡烛 j 内的 SellStop / BuyStop。只要 WantedPrice 落在当前 K 线的 High[i]~Low[i] 区间,虚拟单即转为 STATUS_MARKET 并记录触发索引 IndexMarket=i。外汇与贵金属市场跳空频繁,这种区间命中判断比点价等于更贴近真实成交可能。 直接把下面代码贴进 MT5 脚本跑一遍,把 BarsExpirationOpen 从 5 改到 20,能看到作废比例明显下降,这是验证衰减窗口的最快路径。

MQL5 / C++
if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_VIRTUAL )
  {
   if ( BarOrders[j].SellLimitOrders[k].IndexMarket - class="num">1 >= BarOrders[j].SellLimitOrders[k].BarsExpirationOpen  )
     BarOrders[j].SellLimitOrders[k].Status=STATUS_ABORTED;
   else
     {
      if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha > class="num">0.0 )
        BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart/class="type">class="kw">double(BarOrders[j].SellLimitOrders[k].BarsExpirationOpen);
      if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha < class="num">0.0 ) BarOrders[j].SellLimitOrders[k].VolumeAlpha=class="num">0.0;
     }
  }

if ( VolumeAlphaStop != class="num">0.0 )
  {
   for ( class="type">int i=SizeBarOrders; i>class="num">0; i-- )
     {
      for ( class="type">int j=SizeBarOrders-class="num">1; j>i; j-- )
        {
         for ( class="type">int k=class="num">0; k<size; k++ )
           {
            if ( BarOrders[j].SellStopOrders[k].Status == STATUS_VIRTUAL
            && BarOrders[j].SellStopOrders[k].WantedPrice <= High[i]
            && BarOrders[j].SellStopOrders[k].WantedPrice >= Low[i] )
              {
               BarOrders[j].SellStopOrders[k].Status = STATUS_MARKET;
               BarOrders[j].SellStopOrders[k].IndexMarket = i;
              }
            if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_VIRTUAL
            && BarOrders[j].BuyStopOrders[k].WantedPrice <= High[i]
            && BarOrders[j].BuyStopOrders[k].WantedPrice >= Low[i] )

◍ 挂单未触发时的衰减与作废逻辑

在模拟挂单生命周期时,若 BuyStop 在虚拟态未触发,需要按柱数线性衰减其影响力权重 VolumeAlpha,并在超过有效期后直接标记作废。 具体判定用 IndexMarket-1 与 BarsExpirationOpen 比较:当前者大于等于后者,状态置为 STATUS_ABORTED;否则每根 K 线扣除 VolumeStart 均摊到有效柱数的值,且下限钳制在 0.0。 SellStop 走完全一致的分支,只是字段换成 SellStopOrders[k]。实盘外汇与贵金属波动大、滑点不可控,这种虚拟衰减仅用于回测推演,实盘挂单可能因跳空直接越过触发价,概率上未必与模拟一致。 把下面这段塞进 EA 的逐 Bar 扫描循环,就能在 MT5 里观察虚拟挂单如何随柱衰减:

MQL5 / C++
{
   BarOrders[j].BuyStopOrders[k].Status = STATUS_MARKET;
   BarOrders[j].BuyStopOrders[k].IndexMarket = i;
}

class=class="str">"cmt">/////// Check for interest expiration of stop and limit players
if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_VIRTUAL )class=class="str">"cmt">//
   {
    if ( BarOrders[j].BuyStopOrders[k].IndexMarket - class="num">1 >= BarOrders[j].BuyStopOrders[k].BarsExpirationOpen  )
    BarOrders[j].BuyStopOrders[k].Status=STATUS_ABORTED;
    else
      {
       if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha > class="num">0.0 )
       BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart/class="type">class="kw">double(BarOrders[j].BuyStopOrders[k].BarsExpirationOpen);
       if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha < class="num">0.0 ) BarOrders[j].BuyStopOrders[k].VolumeAlpha=class="num">0.0;
      }
    }
if ( BarOrders[j].SellStopOrders[k].Status == STATUS_VIRTUAL )class=class="str">"cmt">//
   {
    if ( BarOrders[j].SellStopOrders[k].IndexMarket - class="num">1 >= BarOrders[j].SellStopOrders[k].BarsExpirationOpen  )
    BarOrders[j].SellStopOrders[k].Status=STATUS_ABORTED;
    else
      {
       if ( BarOrders[j].SellStopOrders[k].VolumeAlpha > class="num">0.0 )
       BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart/class="type">class="kw">double(BarOrders[j].SellStopOrders[k].BarsExpirationOpen);
       if ( BarOrders[j].SellStopOrders[k].VolumeAlpha < class="num">0.0 ) BarOrders[j].SellStopOrders[k].VolumeAlpha=class="num">0.0;
      }
    }

市价单在逐根K线上的 alpha 衰减

UpdateMarket() 负责把已成交的 BuyLimit / SellLimit 模拟单按后续每根 K 线的价格波动做状态刷新。只有当 VolumeAlphaLimit 不为 0 时,才进入三层嵌套循环:先倒序遍历当前 K 线之前的所有 Bar,再对每根 Bar 内的挂单数组做体积重算。 内层逻辑只对 Status 等于 STATUS_MARKET 的订单生效。以多单为例,若 VolumeAlpha 仍大于 0,则分别按 (High[i]-Open[i]) 与 (Open[i]-Low[i]) 占盈利平仓价、止损价区间的比例,扣减初始 VolumeStart 的对应份额——一段算浮盈消耗,一段算浮亏消耗。 衰减后若 VolumeAlpha 跌到负值,直接归零,意味着该模拟仓在这根 K 线内已耗尽。外汇与贵金属杠杆品种中,这种按影线区间线性摊销的假设和实际滑点偏差可能较大,开 MT5 把 UpPriceToClose 与 LowPriceToClose 拉出来比对真实 tick 回放,能看出模型在快速反转日的低估倾向。

MQL5 / C++
class="type">void UpdateMarket()class=class="str">"cmt">// update the status of market orders
  {
  class="type">int size=ArraySize(BarOrders[class="num">0].BuyLimitOrders);
  class="type">int SizeBarOrders=ArraySize(BarOrders);
  
  if ( VolumeAlphaLimit != class="num">0.0 )
    {
    for ( class="type">int i=SizeBarOrders; i>class="num">1; i-- )class=class="str">"cmt">// update the state of orders simulating each candlestick
      {
      for ( class="type">int j=SizeBarOrders-class="num">1; j>i; j-- )class=class="str">"cmt">// update the state of all candlesticks preceding this one
        {
        for ( class="type">int k=class="num">0; k<size; k++ )class=class="str">"cmt">// update the state inside each preceding candlestick
          {
          class=class="str">"cmt">// Block for closing when prices change
          if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_MARKET )class=class="str">"cmt">//
            {
            if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha > class="num">0.0 )
              {
              BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart*(High[i]-Open[i])/(BarOrders[j].BuyLimitOrders[k].UpPriceToClose-BarOrders[j].BuyLimitOrders[k].WantedPrice);class=class="str">"cmt">// with profit
              BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart*(Open[i]-Low[i])/(BarOrders[j].BuyLimitOrders[k].WantedPrice-BarOrders[j].BuyLimitOrders[k].LowPriceToClose);class=class="str">"cmt">// with loss
              }
            if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha < class="num">0.0 ) BarOrders[j].BuyLimitOrders[k].VolumeAlpha=class="num">0.0;
            }
          if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_MARKET )class=class="str">"cmt">//
            {
            if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha > class="num">0.0 )
              {

「挂单残余量的时间与价格双重衰减」

限价单进入市价状态后,残余持仓量 VolumeAlpha 并不是静止的,它同时受价格穿越幅度与到期时间两个维度挤压。价格维度上,买限价单若被向下刺穿,按 (Open-Low)/(WantedPrice-LowPriceToClose) 的比例扣减初始量;卖限价单则分盈利段 (Open-Low)/(WantedPrice-LowPriceToClose) 与亏损段 (High-Open)/(UpPriceToClose-WantedPrice) 两段分别计算,任何情况下扣减后若小于 0 则强制归零。 时间维度上,系统另有一套按 K 线数线性衰减的逻辑:若订单状态为 STATUS_MARKET 且 VolumeAlpha 仍大于 0,则每根新 K 线减去 VolumeStart / BarsExpirationClose。例如 BarsExpirationClose=10 时,未被动用的挂单残余会沿时间轴以每根 K 线 10% 初始量的速度消退。 外层再以 VolumeAlphaStop!=0 为开关,用双重倒序循环(i 从 SizeBarOrders 到 2,j 从 SizeBarOrders-1 到 i)逐根重算前置 K 线上的订单状态。外汇与贵金属市场跳空频繁,这类双重衰减在实盘可能放大滑点下的量误判,建议直接把这段代码塞进 MT5 脚本,用历史 tick 跑一遍看残余量曲线。

MQL5 / C++
BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart*(Open[i]-Low[i])/(BarOrders[j].SellLimitOrders[k].WantedPrice-BarOrders[j].SellLimitOrders[k].LowPriceToClose); class=class="str">"cmt">//with profit
BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart*(High[i]-Open[i])/(BarOrders[j].SellLimitOrders[k].UpPriceToClose-BarOrders[j].SellLimitOrders[k].WantedPrice); class=class="str">"cmt">//with loss
}
if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha < class="num">0.0 ) BarOrders[j].SellLimitOrders[k].VolumeAlpha=class="num">0.0;
}
class=class="str">"cmt">// End of lock for closing when prices change

class=class="str">"cmt">// Block for closing when time changes******************************************************
if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_MARKET )class=class="str">"cmt">//
  {
  if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha > class="num">0.0 )
  BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart/class="type">class="kw">double(BarOrders[j].BuyLimitOrders[k].BarsExpirationClose);
  if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha < class="num">0.0 ) BarOrders[j].BuyLimitOrders[k].VolumeAlpha=class="num">0.0;
  }
if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_MARKET )class=class="str">"cmt">//
  {
  if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha > class="num">0.0 )
  BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart/class="type">class="kw">double(BarOrders[j].SellLimitOrders[k].BarsExpirationClose);
  if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha < class="num">0.0 ) BarOrders[j].SellLimitOrders[k].VolumeAlpha=class="num">0.0;
  }
}
   }
  }
 }

if ( VolumeAlphaStop != class="num">0.0 )
  {
  for ( class="type">int i=SizeBarOrders; i>class="num">1; i-- )class=class="str">"cmt">// update the state of orders simulating each candlestick
   {
   for ( class="type">int j=SizeBarOrders-class="num">1; j>i; j-- )class=class="str">"cmt">// update the state of all candlesticks preceding this one
    {

◍ 挂单成交量随K线波动的衰减算法

在回测引擎里,每一根已成交的挂单不会静止地躺在历史里,价格每推进一个 tick,它剩余未平仓的虚拟成交量就要按当前K线的波动重新折算。下面这段循环就是干这个活的:遍历前面每根K线里所有的 BuyStop / SellStop,只要状态是 STATUS_MARKET 且还有 VolumeAlpha 剩余,就扣减。 扣减逻辑分两块:价格变动块和时间变动块。以 BuyStop 为例,盈利部分按 (High[i]-Open[i]) 占目标价区间的比例来减,亏损部分按 (Open[i]-Low[i]) 占止损区间的比例来减;SellStop 镜像处理。减完若 VolumeAlpha 掉到负值,直接钳到 0.0,避免穿仓式的负数挂单。 这套写法的实战意义是:你能在 MT5 里用历史数据跑出「某价位挂单在当前K线内被吃掉多少」的曲线,而不是只看到成交/未成交两个状态。外汇与贵金属杠杆高,这类虚拟成交推算仅作流动性参考,不代表真实盘口撮合。

MQL5 / C++
for ( class="type">int k=class="num">0; k<size; k++ )class=class="str">"cmt">// update the state inside each preceding candlestick
    {
    class=class="str">"cmt">// Block for closing when prices change
    if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_MARKET )class=class="str">"cmt">//
      {
      if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha > class="num">0.0 )
        {
        BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart*(High[i]-Open[i])/(BarOrders[j].BuyStopOrders[k].UpPriceToClose-BarOrders[j].BuyStopOrders[k].WantedPrice);class=class="str">"cmt">// with profit
        BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart*(Open[i]-Low[i])/(BarOrders[j].BuyStopOrders[k].WantedPrice-BarOrders[j].BuyStopOrders[k].LowPriceToClose);class=class="str">"cmt">// with loss
        }
      if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha < class="num">0.0 ) BarOrders[j].BuyStopOrders[k].VolumeAlpha=class="num">0.0;
      }
    if ( BarOrders[j].SellStopOrders[k].Status == STATUS_MARKET )class=class="str">"cmt">//
      {
      if ( BarOrders[j].SellStopOrders[k].VolumeAlpha > class="num">0.0 )
        {
        BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart*(Open[i]-Low[i])/(BarOrders[j].SellStopOrders[k].WantedPrice-BarOrders[j].SellStopOrders[k].LowPriceToClose);class=class="str">"cmt">//with profit
        BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart*(High[i]-Open[i])/(BarOrders[j].SellStopOrders[k].UpPriceToClose-BarOrders[j].SellStopOrders[k].WantedPrice);class=class="str">"cmt">//with loss
        }
      if ( BarOrders[j].SellStopOrders[k].VolumeAlpha < class="num">0.0 ) BarOrders[j].SellStopOrders[k].VolumeAlpha=class="num">0.0;
      }
    class=class="str">"cmt">// End of lock for closing when prices change
    class=class="str">"cmt">// Block for closing when time changes******************************************************
    if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_MARKET )class=class="str">"cmt">//
      {
      if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha > class="num">0.0 )

挂单衰减与市价单逐根消化的量模实现

在模拟引擎里,止损挂单(BuyStop / SellStop)一旦处于 STATUS_MARKET 状态,会按 BarsExpirationClose 根数做线性衰减:每根 K 线扣减 VolumeStart 除以该到期根数,且不允许 VolumeAlpha 跌成负数,最低截断到 0.0。 市价单的处理更细。若某前序 K 线 j 上 BuyMarketOrders[k] 的剩余量 VolumeAlpha 大于 0,则在本根 i 的高开差(High[i]-Open[i])段按盈利比例扣减,在开低差(Open[i]-Low[i])段按亏损比例扣减,分母分别用 UpPriceToClose-WantedPrice 与 WantedPrice-LowPriceToClose 归一。 外层循环从 SizeBarOrders 倒序到 2,再对 j 从 SizeBarOrders-1 扫到 i,最后 k 从 0 到 size,三层嵌套把每根前序蜡烛内的挂单与市价单状态全部重算。外汇与贵金属波动大,这种逐根重演对滑点和跳空敏感,回测结果仅代表历史概率,实盘高风险。 直接把下面片段丢进 MT5 的 EA 模拟模块,改 BarsExpirationClose 或 UpPriceToClose 间距,能立刻看到衰减曲线变化。

MQL5 / C++
BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart/class="type">class="kw">double(BarOrders[j].BuyStopOrders[k].BarsExpirationClose);
if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha < class="num">0.0 ) BarOrders[j].BuyStopOrders[k].VolumeAlpha=class="num">0.0;
}
if ( BarOrders[j].SellStopOrders[k].Status == STATUS_MARKET )class=class="str">"cmt">//
  {
  if ( BarOrders[j].SellStopOrders[k].VolumeAlpha > class="num">0.0 )
  BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart/class="type">class="kw">double(BarOrders[j].SellStopOrders[k].BarsExpirationClose);
  if ( BarOrders[j].SellStopOrders[k].VolumeAlpha < class="num">0.0 ) BarOrders[j].SellStopOrders[k].VolumeAlpha=class="num">0.0;
  }
 }
    }
  }

if ( VolumeAlphaMarket != class="num">0.0 )
  {
  for ( class="type">int i=SizeBarOrders; i>class="num">1; i-- )class=class="str">"cmt">// update the state of orders simulating each candlestick
    {
    for ( class="type">int j=SizeBarOrders-class="num">1; j>i; j-- )class=class="str">"cmt">// update the state of all candlesticks preceding this one
      {
      for ( class="type">int k=class="num">0; k<size; k++ )class=class="str">"cmt">// update the state inside each preceding candlestick
        {
        class=class="str">"cmt">// Block for closing when prices change
        class=class="str">"cmt">/// For obviously market positions
        if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha > class="num">0.0 )
          {
          BarOrders[j].BuyMarketOrders[k].VolumeAlpha-=BarOrders[j].BuyMarketOrders[k].VolumeStart*(High[i]-Open[i])/(BarOrders[j].BuyMarketOrders[k].UpPriceToClose-BarOrders[j].BuyMarketOrders[k].WantedPrice);class=class="str">"cmt">// with profit
          BarOrders[j].BuyMarketOrders[k].VolumeAlpha-=BarOrders[j].BuyMarketOrders[k].VolumeStart*(Open[i]-Low[i])/(BarOrders[j].BuyMarketOrders[k].WantedPrice-BarOrders[j].BuyMarketOrders[k].LowPriceToClose);class=class="str">"cmt">// with loss
          }
        if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha < class="num">0.0 ) BarOrders[j].BuyMarketOrders[k].VolumeAlpha=class="num">0.0;

        if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha > class="num">0.0 )

「挂单与市价单的残余量随行情衰减」

这段逻辑处理虚拟订单在价格和时间两个维度上的「未成交残余量」VolumeAlpha。卖单若当前 bar 的 Open 到 Low 覆盖了 WantedPrice 到 LowPriceToClose 区间,按 (Open-Low)/(WantedPrice-LowPriceToClose) 比例扣减,视为部分止盈;若 High 到 Open 覆盖了 UpPriceToClose 到 WantedPrice,则按另一比例扣减,视为部分止损。 扣减后若 VolumeAlpha 掉到 0 以下,直接钳制为 0.0,避免负数残余污染后续撮合。时间维度上,若 Buy/Sell 市价单的 VolumeAlpha 仍大于 0,每根 bar 固定减去 VolumeStart / BarsExpirationClose,相当于把订单生命周期均摊到存续根数里慢慢蒸发。 UpdateVirtualFast 里用 ArraySize 取 BarOrders 与首根 K 线的 BuyLimitOrders 长度,仅当 VolumeAlphaLimit != 0 才反向遍历历史 bar 与内部挂单做状态刷新。外汇与贵金属杠杆高,这类虚拟撮合只是回测近似,实盘滑点可能让残余量归零速度明显更快。

MQL5 / C++
{
   BarOrders[j].SellMarketOrders[k].VolumeAlpha-=BarOrders[j].SellMarketOrders[k].VolumeStart*(Open[i]-Low[i])/(BarOrders[j].SellMarketOrders[k].WantedPrice-BarOrders[j].SellMarketOrders[k].LowPriceToClose);class=class="str">"cmt">// with profit
   BarOrders[j].SellMarketOrders[k].VolumeAlpha-=BarOrders[j].SellMarketOrders[k].VolumeStart*(High[i]-Open[i])/(BarOrders[j].SellMarketOrders[k].UpPriceToClose-BarOrders[j].SellMarketOrders[k].WantedPrice);class=class="str">"cmt">// with loss
   }
   if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha < class="num">0.0 ) BarOrders[j].SellMarketOrders[k].VolumeAlpha=class="num">0.0;
   class=class="str">"cmt">// End of lock for closing when prices change

   class=class="str">"cmt">// Block for closing when time changes******************************************************

   class=class="str">"cmt">/// For obviously market positions
   if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha > class="num">0.0 )
   BarOrders[j].BuyMarketOrders[k].VolumeAlpha-=BarOrders[j].BuyMarketOrders[k].VolumeStart/class="type">class="kw">double(BarOrders[j].BuyMarketOrders[k].BarsExpirationClose);
   if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha < class="num">0.0 ) BarOrders[j].BuyMarketOrders[k].VolumeAlpha=class="num">0.0;
   if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha > class="num">0.0 )
   BarOrders[j].SellMarketOrders[k].VolumeAlpha-=BarOrders[j].SellMarketOrders[k].VolumeStart/class="type">class="kw">double(BarOrders[j].SellMarketOrders[k].BarsExpirationClose);
   if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha < class="num">0.0 ) BarOrders[j].SellMarketOrders[k].VolumeAlpha=class="num">0.0;
   class=class="str">"cmt">//
   }
   } 
   }
   }

class=class="str">"cmt">/// fast methods****
class="type">void UpdateVirtualFast()class=class="str">"cmt">// update the status of class="kw">virtual orders
  {
  class="type">int SizeBarOrders=ArraySize(BarOrders);
  class="type">int size=ArraySize(BarOrders[class="num">0].BuyLimitOrders);

  if ( VolumeAlphaLimit != class="num">0.0 )
   {
   for ( class="type">int j=SizeBarOrders-class="num">1; j>class="num">0; j-- )class=class="str">"cmt">// update the state of all candlesticks preceding this one
    {
    for ( class="type">int k=class="num">0; k<size; k++ )class=class="str">"cmt">// update the state inside each preceding candlestick
      {

◍ 虚拟挂单的触发与衰减逻辑

在基于 K 线桶的订单模拟里,虚拟限价单不会一直挂着。若某根已完成 K 线(索引 1)的高点 Low[1] 到 High[1] 区间覆盖了挂单的 WantedPrice,且该单仍处于 STATUS_VIRTUAL,则立刻翻成市价单并标记 IndexMarket = 1,买限和卖限对称处理。 对未触发的虚拟单,还要跑过期检查。以买限为例:当 IndexMarket - 1 大于等于 BarsExpirationOpen 时,状态置为 STATUS_ABORTED;否则按 BarsExpirationOpen 均分初始量做衰减,VolumeAlpha 每根减 VolumeStart / double(BarsExpirationOpen),跌到 0 以下就钳回 0.0。卖限同构。 这套机制意味着:外汇与贵金属这类高波动品种里,虚拟流动性会随 K 线消耗而递减,回测时若 BarsExpirationOpen 设 5,第 6 根未触碰即作废,实际成交概率倾向低于永续挂单假设。

MQL5 / C++
if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_VIRTUAL
 && BarOrders[j].BuyLimitOrders[k].WantedPrice <= High[class="num">1]
 && BarOrders[j].BuyLimitOrders[k].WantedPrice >= Low[class="num">1] )class=class="str">"cmt">// if the order is class="kw">virtual and is inside a candlestick, then it turns into a market one
              {
              BarOrders[j].BuyLimitOrders[k].Status = STATUS_MARKET;
              BarOrders[j].BuyLimitOrders[k].IndexMarket = class="num">1;
              }
             if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_VIRTUAL
 && BarOrders[j].SellLimitOrders[k].WantedPrice <= High[class="num">1]
 && BarOrders[j].SellLimitOrders[k].WantedPrice >= Low[class="num">1] )class=class="str">"cmt">// the same
              {
              BarOrders[j].SellLimitOrders[k].Status = STATUS_MARKET;
              BarOrders[j].SellLimitOrders[k].IndexMarket = class="num">1;
              }

              class=class="str">"cmt">/////// Check for interest expiration of limit players
             if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_VIRTUAL )class=class="str">"cmt">//
              {
              if ( BarOrders[j].BuyLimitOrders[k].IndexMarket - class="num">1 >= BarOrders[j].BuyLimitOrders[k].BarsExpirationOpen )
              BarOrders[j].BuyLimitOrders[k].Status=STATUS_ABORTED;
              else
                {
                 if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha > class="num">0.0 )
                 BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart/class="type">class="kw">double(BarOrders[j].BuyLimitOrders[k].BarsExpirationOpen);
                 if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha < class="num">0.0 ) BarOrders[j].BuyLimitOrders[k].VolumeAlpha=class="num">0.0;
                 }
              }
             if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_VIRTUAL )class=class="str">"cmt">//
              {
              if ( BarOrders[j].SellLimitOrders[k].IndexMarket - class="num">1 >= BarOrders[j].SellLimitOrders[k].BarsExpirationOpen  )
              BarOrders[j].SellLimitOrders[k].Status=STATUS_ABORTED;
              else
                {
                 if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha > class="num">0.0 )

挂单兴趣随K线衰减与触发判定

限价卖单的剩余兴趣 VolumeAlpha 按已过期K线数线性扣减:每根超过挂单寿命的K线,扣掉初始量 VolumeStart 除以寿命根数 BarsExpirationOpen。若扣减后为负,直接归零,避免负值污染后续撮合概率。 当检测到 VolumeAlphaStop 非零,说明上一根K线(索引1)有止损类挂单需要回看。代码逆序遍历此前所有K线(j 从 SizeBarOrders-1 到 1),再遍历每根内 size 个挂单槽位 k。 对于仍处 STATUS_VIRTUAL 的 SellStop / BuyStop,只要其 WantedPrice 落在上根 High[1]~Low[1] 区间,就改为 STATUS_MARKET 且记 IndexMarket=1,表示被影线触发。外汇与贵金属杠杆高,这类虚拟止损触发仅反映模型兴趣,实盘滑点可能让成交价偏离。 随后检查停止类买停挂单的兴趣过期:若 IndexMarket-1 已达 BarsExpirationOpen,状态置为 STATUS_ABORTED;否则在 VolumeAlpha 仍大于 0 时继续衰减逻辑,保证早挂的单随时间淡出。

MQL5 / C++
BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart/class="type">class="kw">double(BarOrders[j].SellLimitOrders[k].BarsExpirationOpen);
if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha < class="num">0.0 ) BarOrders[j].SellLimitOrders[k].VolumeAlpha=class="num">0.0;
}
}
}
}

if ( VolumeAlphaStop != class="num">0.0 )
  {
  for ( class="type">int j=SizeBarOrders-class="num">1; j>class="num">0; j-- )class=class="str">"cmt">// update the state of all candlesticks preceding this one
    {
    for ( class="type">int k=class="num">0; k<size; k++ )class=class="str">"cmt">// update the state inside each preceding candlestick
      {
      if ( BarOrders[j].SellStopOrders[k].Status == STATUS_VIRTUAL
      && BarOrders[j].SellStopOrders[k].WantedPrice <= High[class="num">1]
      && BarOrders[j].SellStopOrders[k].WantedPrice >= Low[class="num">1] )class=class="str">"cmt">// the same
        {
        BarOrders[j].SellStopOrders[k].Status = STATUS_MARKET;
        BarOrders[j].SellStopOrders[k].IndexMarket = class="num">1;
        }
      if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_VIRTUAL
      && BarOrders[j].BuyStopOrders[k].WantedPrice <= High[class="num">1]
      && BarOrders[j].BuyStopOrders[k].WantedPrice >= Low[class="num">1] )class=class="str">"cmt">// the same
        {
        BarOrders[j].BuyStopOrders[k].Status = STATUS_MARKET;
        BarOrders[j].BuyStopOrders[k].IndexMarket = class="num">1;
        }

      class=class="str">"cmt">/////// Check for interest expiration of stop players
      if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_VIRTUAL )class=class="str">"cmt">//
        {
        if ( BarOrders[j].BuyStopOrders[k].IndexMarket - class="num">1 >= BarOrders[j].BuyStopOrders[k].BarsExpirationOpen  )
        BarOrders[j].BuyStopOrders[k].Status=STATUS_ABORTED;
        else
          {
          if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha > class="num">0.0 )

「挂单衰减与状态切换的底层循环」

这段逻辑处理的是虚拟挂单随时间推移的成交量衰减与作废判定。以 BuyStop 为例,若挂单处于 STATUS_VIRTUAL,且当前 K 线索引距离开单已过的 BarsExpirationOpen 根数未超限,就按 VolumeStart 除以 BarsExpirationOpen 的均量逐步扣减 VolumeAlpha;一旦扣减后小于 0.0,直接钳制为 0.0,避免负成交量参与后续撮合。 SellStop 分支完全对称:当 IndexMarket-1 大于等于 BarsExpirationOpen 时,状态置为 STATUS_ABORTED 终止虚拟挂单;否则同样做线性衰减。这种按根数均摊的做法,意味着若 BarsExpirationOpen=10、VolumeStart=1.0,则每根 K 线 VolumeAlpha 减少 0.1,第 10 根后归零。 UpdateMarketFast 函数另起一套扫描:先取 BarOrders[0].BuyLimitOrders 的大小与 BarOrders 总大小,当 VolumeAlphaLimit 非零时,从倒数第二根 K 线向前回溯,逐根、逐挂单检查 STATUS_MARKET 的平仓块。外汇与贵金属市场跳空频繁,这类状态机在实盘里可能因滑点导致 Status 切换滞后,建议开 MT5 用策略测试器以 Tick 模式跑一遍观察 VolumeAlpha 曲线。

MQL5 / C++
BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart/class="type">class="kw">double(BarOrders[j].BuyStopOrders[k].BarsExpirationOpen);
if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha < class="num">0.0 ) BarOrders[j].BuyStopOrders[k].VolumeAlpha=class="num">0.0;
}
if ( BarOrders[j].SellStopOrders[k].Status == STATUS_VIRTUAL )
{
if ( BarOrders[j].SellStopOrders[k].IndexMarket - class="num">1 >= BarOrders[j].SellStopOrders[k].BarsExpirationOpen )
BarOrders[j].SellStopOrders[k].Status=STATUS_ABORTED;
else
  {
  if ( BarOrders[j].SellStopOrders[k].VolumeAlpha > class="num">0.0 )
  BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart/class="type">class="kw">double(BarOrders[j].SellStopOrders[k].BarsExpirationOpen);
  if ( BarOrders[j].SellStopOrders[k].VolumeAlpha < class="num">0.0 ) BarOrders[j].SellStopOrders[k].VolumeAlpha=class="num">0.0;
  }
}
}
}
}
class="type">void UpdateMarketFast()class=class="str">"cmt">// update the status of market orders
{
class="type">int size=ArraySize(BarOrders[class="num">0].BuyLimitOrders);
class="type">int SizeBarOrders=ArraySize(BarOrders);
if ( VolumeAlphaLimit != class="num">0.0 )
  {
  for ( class="type">int j=SizeBarOrders-class="num">1; j>class="num">0; j-- )
    {
    for ( class="type">int k=class="num">0; k<size; k++ )
      {
      if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_MARKET )
        {

◍ 挂单转市价后的剩余量衰减逻辑

当限价单状态切到 STATUS_MARKET 后,算法不再把它当静态挂单,而是按上一根 K 线的真实波动来侵蚀剩余成交量 VolumeAlpha。买限价单的盈利侵蚀用 (High[1]-Open[1]) 除以 (UpPriceToClose-WantedPrice),亏损侵蚀用 (Open[1]-Low[1]) 除以 (WantedPrice-LowPriceToClose),两笔相减后若 VolumeAlpha 跌成负值就强行归零。 卖限价单镜像处理:盈利侵蚀走 (Open[1]-Low[1])/(WantedPrice-LowPriceToClose),亏损侵蚀走 (High[1]-Open[1])/(UpPriceToClose-WantedPrice)。这种按价格区间比例扣减的方式,意味着波动越大、离平仓边界越近,单根 K 线吃掉的量越多。 除了价格变动,时间也会单独扣量。若 BarsExpirationClose 设为 10,则每根 K 线固定减去 VolumeStart/10.0,跟行情幅度无关,纯粹按寿命摊销。外汇与贵金属杠杆高,这类内部成交量衰减模型只是策略记账,实盘撮合仍受点差与滑点干扰,回测盈利倾向偏乐观。 下面这段是买/卖限价单在市价态下的核心扣减代码,可直接拷进 MT5 看结构体字段是否对得上你自己的订单池。

MQL5 / C++
if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha > class="num">0.0 )
              {
              BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart*(High[class="num">1]-Open[class="num">1])/(BarOrders[j].BuyLimitOrders[k].UpPriceToClose-BarOrders[j].BuyLimitOrders[k].WantedPrice);class=class="str">"cmt">// with profit
              BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart*(Open[class="num">1]-Low[class="num">1])/(BarOrders[j].BuyLimitOrders[k].WantedPrice-BarOrders[j].BuyLimitOrders[k].LowPriceToClose);class=class="str">"cmt">// with loss
              }
              if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha < class="num">0.0 ) BarOrders[j].BuyLimitOrders[k].VolumeAlpha=class="num">0.0;
              }
              if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_MARKET )class=class="str">"cmt">//
              {
              if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha > class="num">0.0 )
              {
              BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart*(Open[class="num">1]-Low[class="num">1])/(BarOrders[j].SellLimitOrders[k].WantedPrice-BarOrders[j].SellLimitOrders[k].LowPriceToClose);class=class="str">"cmt">// with profit
              BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart*(High[class="num">1]-Open[class="num">1])/(BarOrders[j].SellLimitOrders[k].UpPriceToClose-BarOrders[j].SellLimitOrders[k].WantedPrice);class=class="str">"cmt">// with loss
              }
              if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha < class="num">0.0 ) BarOrders[j].SellLimitOrders[k].VolumeAlpha=class="num">0.0;
              }
              class=class="str">"cmt">// End of lock for closing when prices change
              
              class=class="str">"cmt">// Block for closing when time changes******************************************************
              if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_MARKET )class=class="str">"cmt">//
              {
              if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha > class="num">0.0 )
              BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart/class="type">class="kw">double(BarOrders[j].BuyLimitOrders[k].BarsExpirationClose);
              if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha < class="num">0.0 ) BarOrders[j].BuyLimitOrders[k].VolumeAlpha=class="num">0.0;
              }
              if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_MARKET )class=class="str">"cmt">//
              {
              if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha > class="num">0.0 )

挂单残余量的动态衰减算法

在回测框架里,每一根历史 K 线都挂着一组未成交挂单的结构体。当价格推进到下一根(索引 1)时,前一根里还没耗尽的 SellLimit 量会按存续 K 线数做线性衰减:每次减去初始量除以到期根数,若算出来低于 0 就直接钳到 0.0。 如果触发的是 BuyStop 且已转为市价状态,衰减逻辑就换成按上一根实体区间拆账。盈利部分用 (High[1]-Open[1]) 除以(平仓上价-触发价)来扣,亏损部分用 (Open[1]-Low[1]) 除以(触发价-平仓下价)来扣,两个比值分别乘初始量后从 VolumeAlpha 里减。SellStop 市价态同理,只是分子分母的上下边界互换。 这段代码的实战意义在于:你能在 MT5 里把 VolumeAlpha 打印到注释面板,观察一根 15 分钟 K 线内挂单残余量从 1.0 衰减到 0.3 的过程。外汇与贵金属杠杆高,这类模拟量仅反映概率分布,不等于真实成交。

MQL5 / C++
BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart/class="type">class="kw">double(BarOrders[j].SellLimitOrders[k].BarsExpirationClose);
if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha < class="num">0.0 ) BarOrders[j].SellLimitOrders[k].VolumeAlpha=class="num">0.0;

if ( VolumeAlphaStop != class="num">0.0 )
  {
  for ( class="type">int j=SizeBarOrders-class="num">1; j>class="num">0; j-- )
    {
    for ( class="type">int k=class="num">0; k<size; k++ )
      {
      if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_MARKET )
        {
        if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha > class="num">0.0 )
          {
          BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart*(High[class="num">1]-Open[class="num">1])/(BarOrders[j].BuyStopOrders[k].UpPriceToClose-BarOrders[j].BuyStopOrders[k].WantedPrice);
          BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart*(Open[class="num">1]-Low[class="num">1])/(BarOrders[j].BuyStopOrders[k].WantedPrice-BarOrders[j].BuyStopOrders[k].LowPriceToClose);
          }
        if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha < class="num">0.0 ) BarOrders[j].BuyStopOrders[k].VolumeAlpha=class="num">0.0;
        }
      if ( BarOrders[j].SellStopOrders[k].Status == STATUS_MARKET )
        {
        if ( BarOrders[j].SellStopOrders[k].VolumeAlpha > class="num">0.0 )
          {
          BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart*(Open[class="num">1]-Low[class="num">1])/(BarOrders[j].SellStopOrders[k].WantedPrice-BarOrders[j].SellStopOrders[k].LowPriceToClose);
          BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart*(High[class="num">1]-Open[class="num">1])/(BarOrders[j].SellStopOrders[k].UpPriceToClose-BarOrders[j].SellStopOrders[k].WantedPrice);
          }

「挂单活性随K线推移的衰减逻辑」

这段逻辑处理的是止损挂单(BuyStop / SellStop)在价格变动与时间变动两种情形下的 VolumeAlpha 衰减。价格变动块里先把负体积钳制为 0.0,避免脏数据污染后续统计;时间变动块则按 BarsExpirationClose 把初始挂单量均摊到每一根到期K线去做递减。 具体看时间衰减:当某 BuyStop 状态为 STATUS_MARKET 且 VolumeAlpha 仍大于 0.0,就执行 VolumeAlpha -= VolumeStart / double(BarsExpirationClose)。SellStop 对称处理。这意味着若 BarsExpirationClose 设为 10,每过一根K线该挂单的活跃权重自动砍掉十分之一,到第 10 根归零。 后面还有一段针对已成交市价单的回溯:遍历当前K线之前的所有Bar,对 BuyMarketOrders 用 (High[1]-Open[1]) 算盈利部分衰减、用 (Open[1]-Low[1]) 算亏损部分衰减,分母分别是 UpPriceToClose-WantedPrice 与 WantedPrice-LowPriceToClose。这种拆法把一根母K线内的双向波动都计进了持仓权重变化,外汇与贵金属波动大时该值可能剧烈跳动,实盘前务必在 MT5 策略测试器跑一遍验证。

MQL5 / C++
if ( BarOrders[j].SellStopOrders[k].VolumeAlpha < class="num">0.0 ) BarOrders[j].SellStopOrders[k].VolumeAlpha=class="num">0.0;

            class=class="str">"cmt">// End of lock for closing when prices change

            class=class="str">"cmt">// Block for closing when time changes******************************************************
            if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_MARKET )class=class="str">"cmt">//
              {
              if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha > class="num">0.0 )
              BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart/class="type">class="kw">double(BarOrders[j].BuyStopOrders[k].BarsExpirationClose);
              if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha < class="num">0.0 ) BarOrders[j].BuyStopOrders[k].VolumeAlpha=class="num">0.0;
              }
            if ( BarOrders[j].SellStopOrders[k].Status == STATUS_MARKET )class=class="str">"cmt">//
              {
              if ( BarOrders[j].SellStopOrders[k].VolumeAlpha > class="num">0.0 )
              BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart/class="type">class="kw">double(BarOrders[j].SellStopOrders[k].BarsExpirationClose);
              if ( BarOrders[j].SellStopOrders[k].VolumeAlpha < class="num">0.0 ) BarOrders[j].SellStopOrders[k].VolumeAlpha=class="num">0.0;
              }
            class=class="str">"cmt">//
            }
            }
            }

         if ( VolumeAlphaMarket != class="num">0.0 )
           {
           for ( class="type">int j=SizeBarOrders-class="num">1; j>class="num">0; j-- )class=class="str">"cmt">// update the state of all candlesticks preceding this one
             {
             for ( class="type">int k=class="num">0; k<size; k++ )class=class="str">"cmt">// update the state inside each preceding candlestick
               {
               class=class="str">"cmt">/// For obviously market positions
               if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha > class="num">0.0 )
                 {
                 BarOrders[j].BuyMarketOrders[k].VolumeAlpha-=BarOrders[j].BuyMarketOrders[k].VolumeStart*(High[class="num">1]-Open[class="num">1])/(BarOrders[j].BuyMarketOrders[k].UpPriceToClose-BarOrders[j].BuyMarketOrders[k].WantedPrice);class=class="str">"cmt">// with profit
                 BarOrders[j].BuyMarketOrders[k].VolumeAlpha-=BarOrders[j].BuyMarketOrders[k].VolumeStart*(Open[class="num">1]-Low[class="num">1])/(BarOrders[j].BuyMarketOrders[k].WantedPrice-BarOrders[j].BuyMarketOrders[k].LowPriceToClose);class=class="str">"cmt">// with loss

◍ 挂单存量随K线推进的衰减逻辑

这段逻辑处理的是模拟盘口里挂单成交后的残留量(VolumeAlpha)如何随价格跳动和时间流逝双重衰减。价格变更时,买入市价单若残留量为负直接归零;卖出市价单则按上一根K线(Open[1]、Low[1]、High[1])相对挂单触发价与止盈止损边界的比例,分别扣减盈利段与亏损段对应的成交量。 时间变更块里,买卖市价单只要残留量大于0,就按 VolumeStart 除以 BarsExpirationClose 做线性递减,例如 BarsExpirationClose=10 时每根K线砍掉初始量的 10%。所有扣减后若算成负值,统一夹回 0.0,避免存量出现非法负手数。 CalculateStartVolume 另起一头,把 BarOrders[0] 的 BuyStopOrders 数组尺寸读出来,并在 VolumeAlphaStop 非零时倒序遍历所有 BarOrders 累加起始总挂单量,作为后续判断市场饱和度的基准。外汇与贵金属杠杆高,这类模拟衰减参数直接上实盘前务必在 MT5 策略测试器里用历史数据核对手数变化曲线。

MQL5 / C++
if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha < class="num">0.0 ) BarOrders[j].BuyMarketOrders[k].VolumeAlpha=class="num">0.0;
if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha > class="num">0.0 )
  {
  BarOrders[j].SellMarketOrders[k].VolumeAlpha-=BarOrders[j].SellMarketOrders[k].VolumeStart*(Open[class="num">1]-Low[class="num">1])/(BarOrders[j].SellMarketOrders[k].WantedPrice-BarOrders[j].SellMarketOrders[k].LowPriceToClose);class=class="str">"cmt">// with profit
  BarOrders[j].SellMarketOrders[k].VolumeAlpha-=BarOrders[j].SellMarketOrders[k].VolumeStart*(High[class="num">1]-Open[class="num">1])/(BarOrders[j].SellMarketOrders[k].UpPriceToClose-BarOrders[j].SellMarketOrders[k].WantedPrice);class=class="str">"cmt">// with loss
  }
if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha < class="num">0.0 ) BarOrders[j].SellMarketOrders[k].VolumeAlpha=class="num">0.0;
class=class="str">"cmt">// End of lock for closing when prices change
class=class="str">"cmt">// Block for closing when time changes******************************************************
class=class="str">"cmt">/// For obviously market positions
if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha > class="num">0.0 )
  BarOrders[j].BuyMarketOrders[k].VolumeAlpha-=BarOrders[j].BuyMarketOrders[k].VolumeStart/class="type">class="kw">double(BarOrders[j].BuyMarketOrders[k].BarsExpirationClose);
if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha < class="num">0.0 ) BarOrders[j].BuyMarketOrders[k].VolumeAlpha=class="num">0.0;
if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha > class="num">0.0 )
  BarOrders[j].SellMarketOrders[k].VolumeAlpha-=BarOrders[j].SellMarketOrders[k].VolumeStart/class="type">class="kw">double(BarOrders[j].SellMarketOrders[k].BarsExpirationClose);
if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha < class="num">0.0 ) BarOrders[j].SellMarketOrders[k].VolumeAlpha=class="num">0.0;
class=class="str">"cmt">//
}
}
}
class=class="str">"cmt">///******
class="type">void CalculateStartVolume()class=class="str">"cmt">// calculate the starting total volume of all positions(relative to it we will estimate market fullness)
{
StartVolume=class="num">0;
class="type">int size=ArraySize(BarOrders[class="num">0].BuyStopOrders);
if ( VolumeAlphaStop != class="num">0.0 )
  {
  for ( class="type">int j=ArraySize(BarOrders)-class="num">1; j>=class="num">0; j-- )

按挂单类型累加起动与实时成交量

这段逻辑把不同类别的买入挂单(止损买、限价买、市价买)分别遍历,把每根 K 线 BarOrders[j] 里对应数组的 VolumeStart 累加到 StartVolume,前提是对应开关 VolumeAlphaStop / VolumeAlphaLimit / VolumeAlphaMarket 不为 0.0。注意 j 的循环方向是从 ArraySize(BarOrders)-1 递减到 0,也就是从最新 Bar 往历史回扫,保证累加顺序和行情推进一致。 CalculateCurrentVolume 则算“当前还活着”的成交量:只挑 Status == STATUS_MARKET 的挂单,把 VolumeAlpha 加进 SummVolumeBuy 或 SummVolumeSell。这里 BuyStop 和 SellStop 共用同一个 i 下标和 size(取的是 BuyStopOrders 的尺寸),若两侧数组长度不一致可能在边界上漏算或越界,开 MT5 跑之前最好先断言两边 ArraySize 相等。 可直接把下面代码贴进 EA 的订单聚合模块验证:把 VolumeAlphaStop 设 1.0、其余设 0.0,用策略测试器看 StartVolume 是否随 BuyStop 挂单递增;再把某单 Status 改成 STATUS_MARKET,观察 SummVolumeBuy 跳变。外汇与贵金属波动剧烈,挂单状态切换频繁,这类统计仅作辅助参考,实盘须自行承担高风险。

MQL5 / C++
{
      for ( class="type">int i=class="num">0; i<size; i++ )
       {
       StartVolume+=BarOrders[j].BuyStopOrders[i].VolumeStart;
       }
      }
      }

      if ( VolumeAlphaLimit != class="num">0.0 )
       {
       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++ )
         {
         StartVolume+=BarOrders[j].BuyLimitOrders[i].VolumeStart;
         }
        }
       }

      if ( VolumeAlphaMarket != class="num">0.0 )
       {
       size=ArraySize(BarOrders[class="num">0].BuyMarketOrders);
       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++ )
         {
         StartVolume+=BarOrders[j].BuyMarketOrders[i].VolumeStart;
         }
        }
       }
      }
      }

   class="type">void CalculateCurrentVolume()class=class="str">"cmt">// calculate the current total volume of all positions
   {
   SummVolumeBuy=class="num">0;
   SummVolumeSell=class="num">0;
   class="type">int size=ArraySize(BarOrders[class="num">0].BuyStopOrders);

   if ( VolumeAlphaStop != class="num">0.0 )
    {
     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++ )
       {
       if ( BarOrders[j].BuyStopOrders[i].Status == STATUS_MARKET )
       SummVolumeBuy+=BarOrders[j].BuyStopOrders[i].VolumeAlpha;
       if ( BarOrders[j].SellStopOrders[i].Status == STATUS_MARKET )
       SummVolumeSell+=BarOrders[j].SellStopOrders[i].VolumeAlpha;
       }
      }
     }
   }

「挂单与市价单的成交量加权汇总」

这段逻辑做的是把限价单和市价单里带 Alpha 权重的成交量分别累加到买卖两侧,方便后续算多空占比。注意它用 VolumeAlphaLimit 和 VolumeAlphaMarket 两个开关隔离了两段统计,避免把未启用的订单类型混进来。 先看限价单部分:遍历 BarOrders 所有柱(j 从末根到 0),对每根柱的 BuyLimitOrders / SellLimitOrders 数组,只累加 Status 为 STATUS_MARKET 的挂单 VolumeAlpha。也就是说,没被激活的挂单不计入累积量。 市价单部分更简单粗暴:同样倒序扫柱,直接把 BuyMarketOrders 和 SellMarketOrders 的 VolumeAlpha 全加进 SummVolumeBuy / SummVolumeSell,不判断状态。 CalculatePercent() 用总成交量做分母算买卖百分比,若买卖总和为 0 则各置 50,避免除零。CalculateRelativeVolume() 用买卖差值除以 StartVolume 得到相对强度,这个比值在 0~1 区间外的概率较低,可用于观察某侧主导程度。 外汇与贵金属杠杆高,这类成交量加权只是市场结构参考,实际方向仍可能反转,请用 MT5 策略测试器跑一段实盘数据验证参数。

MQL5 / C++
if ( VolumeAlphaLimit != class="num">0.0 )
  {
  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++ )
      {
      if ( BarOrders[j].BuyLimitOrders[i].Status == STATUS_MARKET )
      SummVolumeBuy+=BarOrders[j].BuyLimitOrders[i].VolumeAlpha;
      if ( BarOrders[j].SellLimitOrders[i].Status == STATUS_MARKET )
      SummVolumeSell+=BarOrders[j].SellLimitOrders[i].VolumeAlpha;
      }
    }
  }

if ( VolumeAlphaMarket != class="num">0.0 )
  {
  size=ArraySize(BarOrders[class="num">0].BuyMarketOrders);
  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++ )
      {
      SummVolumeBuy+=BarOrders[j].BuyMarketOrders[i].VolumeAlpha;
      SummVolumeSell+=BarOrders[j].SellMarketOrders[i].VolumeAlpha;
      }
    }
  }
}

class="type">void CalculatePercent()class=class="str">"cmt">// calculate the percentage of Buys and Sells relative to all positions
  {
  if ( (SummVolumeBuy+SummVolumeSell) != class="num">0.0 ) BuyPercent=class="num">100.0*SummVolumeBuy/(SummVolumeBuy+SummVolumeSell);
  else BuyPercent=class="num">50;
  if ( (SummVolumeBuy+SummVolumeSell) != class="num">0.0 ) SellPercent=class="num">100.0*SummVolumeSell/(SummVolumeBuy+SummVolumeSell);
  else SellPercent=class="num">50;
  }

class="type">void CalculateRelativeVolume()class=class="str">"cmt">// calculate relative volumes of Buys and Sells(calculate only uncovered part of positions)
  {
  if ( SummVolumeBuy >= SummVolumeSell ) RelativeVolume=(SummVolumeBuy-SummVolumeSell)/StartVolume;
  else RelativeVolume=(SummVolumeSell-SummVolumeBuy)/StartVolume;
  }

};
class="kw">input class="type">bool bPrintE=class="kw">false;class=class="str">"cmt">// print market parameters
class="kw">input CLOSE_MODE CloseModeE=CLOSE_FAST;class=class="str">"cmt">// order closing mode
class="kw">input WORK_MODE ModeE=MODE_SIMPLE;class=class="str">"cmt">// simulation mode
class="kw">input ENUM_GRID_WEIGHT WeightFillingE=WEIGHT_SAME;class=class="str">"cmt">// weight distribution type
class="kw">input class="type">class="kw">double LimitVolumeE=class="num">0.5;class=class="str">"cmt">// significance of limit orders

◍ 挂单簿模拟的参数与触发逻辑

这套订单流模拟把限价、止损与市价三类参与者的权重拆开,用一组 input 直接控制博弈偏向。StopVolumeE 与 MarketVolume 都默认 0.5,意味着在未被显式改参前,止损单和市价单对走廊内供需的牵引力被设为等同,LimitVolumeE 虽未在本段列出但构造 Simulation 时一并传入,三者共同决定半走廊(HalfCorridorE=500 点)内的填充权重。 ExpirationOpenStopBars 与 ExpirationOpenLimitBars 都是 1000 根 K 线,也就是说挂着的止损或限价单若 1000 根内没被触发就自动撤单;而 ExpirationBars=100 指已成交持仓在 100 根内完成到期清算。ProfitPointsCloseE=200、LossPointsCloseE=400 给出止盈止损的基准点数,风险敞口被设为止损是止盈的两倍,属于典型的非对称退出。 Trade() 里先用 Area0==NULL 判断做一次性初始化,把上面十几个参数灌进 Simulation 对象;之后按 ModeE 走 Update 或 UpdateFast 两种刷新路径,后者在样本超过 BarsE=250 根时更省算力。 触发端看 GetBuyPercent() 与 GetSellPercent():当买方占比落在 MinPercentE=60 到 MaxPercentE=80 之间,且 GetRelativeVolume() 在 0.0001~1.00 内,才允许按 bInvert 反向与否去 CloseBuyF/SellF 切换仓位。外汇与贵金属杠杆高,这类百分比阈值只是概率倾向,实盘前务必在 MT5 策略测试器里用历史 tick 跑一遍确认滑点敏感。

MQL5 / C++
class="kw">input class="type">class="kw">double StopVolumeE=class="num">0.5;class=class="str">"cmt">// significance of stop orders
class="kw">input class="type">class="kw">double MarketVolume=class="num">0.5;class=class="str">"cmt">// significance of market orders
class="kw">input class="type">int ExpirationBars=class="num">100;class=class="str">"cmt">// bars for full expiration of open orders
class="kw">input class="type">int ExpirationOpenStopBars=class="num">1000;class=class="str">"cmt">// patience of a stop player in bars, after which the order is canceled
class="kw">input class="type">int ExpirationOpenLimitBars=class="num">1000;class=class="str">"cmt">// patience of a limit player in bars, after which the order is canceled
class="kw">input class="type">int ProfitPointsCloseE=class="num">200;class=class="str">"cmt">// points to close with profit
class="kw">input class="type">int LossPointsCloseE=class="num">400;class=class="str">"cmt">// points to close with loss
class="kw">input class="type">int HalfCorridorE=class="num">500;class=class="str">"cmt">// half-corridor for limit and stop orders
class="kw">input class="type">int OrdersToOneBarE=class="num">50;class=class="str">"cmt">// orders for half-grid per class="num">1 bar
class="kw">input class="type">int BarsE=class="num">250;class=class="str">"cmt">// bars for analysis
class="kw">input class="type">class="kw">double MinPercentE=class="num">60;class=class="str">"cmt">// minimum superiority of one trading side in percentage 
class="kw">input class="type">class="kw">double MaxPercentE=class="num">80;class=class="str">"cmt">// maximum percentage
class="kw">input class="type">class="kw">double MinRelativeVolumeE=class="num">0.0001;class=class="str">"cmt">// minimum market filling [class="num">0...class="num">1]
class="kw">input class="type">class="kw">double MaxRelativeVolumeE=class="num">1.00;class=class="str">"cmt">// maximum market filling [class="num">0...class="num">1]

class="type">void Trade()
  {
  if ( Area0 == NULL )
    {
    CalcAllMQL5Values();
    Area0 = new Simulation(WeightFillingE,HalfCorridorE,OrdersToOneBarE,BarsE
     ,ExpirationOpenLimitBars,ExpirationOpenStopBars,ExpirationBars,ProfitPointsCloseE,LossPointsCloseE
     ,StopVolumeE,LimitVolumeE,MarketVolume);    
    }

  class="kw">switch(ModeE)
    {
    case MODE_SIMPLE:
      Area0.Update();class=class="str">"cmt">// update simulation
    case MODE_FAST:
      Area0.UpdateFast();class=class="str">"cmt">// fast update simulation
    }

  if (bPrintE)
    {
    Print("BuyPercent= ",Area0.GetBuyPercent());
    Print("SellPercent= ",Area0.GetSellPercent());
    Print("RelativeVolume= ",Area0.GetRelativeVolume());
    }
    
  if ( CloseModeE == CLOSE_FAST && Area0.GetBuyPercent() > class="num">50.0 )
    {
    if ( !bInvert ) CloseBuyF();
    else CloseSellF();
    }
    
  if ( CloseModeE == CLOSE_FAST && Area0.GetSellPercent() > class="num">50.0 )
    {
    if ( !bInvert ) CloseSellF();
    else CloseBuyF();
    }    
    
  if ( Area0.GetBuyPercent() > MinPercentE && Area0.GetBuyPercent() < MaxPercentE
  && Area0.GetRelativeVolume() >= MinRelativeVolumeE && Area0.GetRelativeVolume() <= MaxRelativeVolumeE )
    {
    if ( !bInvert )
      {
      CloseBuyF();
      SellF();
      }
    else

卖压区间触发反向开仓的逻辑分支

当监测区卖单占比落在 MinPercentE 与 MaxPercentE 之间,且相对成交量处于 MinRelativeVolumeE 到 MaxRelativeVolumeE 区间时,系统进入卖出信号判定分支。 若未启用反向模式(bInvert 为 false),程序先平掉已有空单再开多单;若启用了反向模式,则平多并开空。这种对称写法让同一组阈值既能做突破跟随,也能做均值反转,具体倾向取决于 bInvert 开关。 外汇与贵金属市场滑点和点差波动大,此类区间触发策略在高波动时段可能频繁假突破,实盘前务必在 MT5 策略测试器用历史数据验证触发频率。

MQL5 / C++
  if ( Area0.GetSellPercent() > MinPercentE && Area0.GetSellPercent() < MaxPercentE
  && Area0.GetRelativeVolume() >= MinRelativeVolumeE && Area0.GetRelativeVolume() <= MaxRelativeVolumeE )
   {
   if ( !bInvert )    
     {
     CloseSellF();
     BuyF();
     }   
   else
     {
     CloseBuyF();
     SellF();
     }
   }

「手动筛设置时先放过低点差低期望的EA」

从我自己的实盘经验看,参数设置最好手动挑,别指望一键优化全交给遗传算法。第一阶段建议直接忽略那些在低周期跑、点差小但数学期望也小的专家顾问信号——它们容易在早期把有效迹象淹没掉,MetaTrader 4 做这种初筛很顺手。 一次成功的搜索后面一定跟着反复修订:调输入、砍冗余、把最终的数学期望和利润系数(也就是信号强度)往上推。理想状态下,每个输入参数的结构应当允许独立运行模式——某个设置改了,系统表现的变化应当尽量不依赖其他参数的值。 这种解耦不是总能实现,但在我们分订单类型逐个分析的做法里是可行的。用平衡设置把各自找到的信号再揉到一起,整体信号质量会被抬上去,而不是被某组耦合参数拖死。外汇与贵金属市场高杠杆、滑点跳空频繁,手动验证前先认清这类筛选只是概率优势,不是确定性。

◍ 用 MT4 先摸信号再上 MT5 压测

这套 EA 是我从零写出来的,逻辑和手头另一个简单版完全不同。核心意图很直接:只要基础认知没跑偏,哪怕模型没完整描述市场物理,也有较高概率跑出点东西。能看出基本性能,就能拆开测、找有效因子,再往下一层走。 测的时候我耗了几天手动筛参数,枯燥但必须自己做。先把单类订单、弱设置跑通,故意不混其他订单类型,这样才能看清某类订单怎么影响信号。MT4 版我挑了最低点差测,因为低周期找模式得看到每一个 tick;MT5 的 MetaTester 会自调点差,反而看不见这个细节。所以起步用 MT4 更稳,直接上 MT5 容易漏掉能当底子的好参数。 我不推荐优化器,本质就是傻瓜式参数迭代。不手动拧过你根本不懂机制——老收音机绑个马达转旋钮,大概率搜不到台,就算蒙到了也说不清是啥。另一个关键是订单量:K线内订单越多,挖出的物理倾向越强,后面才不用老操心性能。但模式可能藏在点差里,不控这块系统就废了。 MT5 的价值在真实 tick 压测,可惜多数品种只有近年的真实报价。我先拿「每个报价点」模式跑旧时段:手数 0.01,数学期望 5 个点,比 MT4 初始 3 个点还高,样本 10 万笔撑着可信度,虽图表丑但信号过点差微利。去年实测手数 0.1,期望 23.4 个点,远好于 MT4 初期,够盈亏平衡有余。 附件里两终端 EA 都能改设置,限价和市场单分开试再组合平均。M5 上表现最好,更高周期我没找到合适参数,其他货币对也没空验,但平缓曲线通常暗示跨品种也行。它不是挂上就爽赚的成品,得你自个儿补订单类型、加过滤、调算法,缺陷我写文时都还留着。外汇贵金属高波动,手动验证前先想清楚点差吞噬。

记住这一条就够了

模拟跑下来,这套基于市场物理直觉的 EA 在 M5 上能给到约 20–40 点的期望,且跨多货币对、贯穿历史样本都站得住。它最大的卖点不是复杂,而是底层逻辑够薄——你永远没法把影响价格的所有因子写进代码,但只要塞进一个哪怕描述不准的因子,信号就可能出现,质量不高也够喝口汤。 别去追那个不存在的「理想公式」。原文作者自己也承认代码里大小缺陷都有,可 EA 照样能跑,剩下的是你自己的改进空间。外汇和贵金属杠杆高、滑点狠,这种薄逻辑系统随时可能被宏观扰动打穿,拿真金白银上前先开 MT5 用 Simulation.zip 里的样本复一遍再说。 下一篇他会换一种更轻更快的多资产分析思路,也照样附 EA。这一篇的价值就到这儿:用最少假设撬动一个能跑的底盘,剩下的现代化留给你。

把重复劳动交给小布
这些诊断与离散导数计算,小布盯盘的 AIGC 已内置,打开对应品种页即可看到盘口与导数轮廓,你只需判断参数区间。

常见问题

价格函数 P(t) 形式不可靠确定,直接建模无意义;其可解析成分体现在变化率上,即 Pa'(t),随机项 Pu'(t) 无法建模,故交易对象应是导数结构。
取一段时间的平均导数 st,对应条形图持续时间,用区间均值逼近连续导数,避开无限时间假设的陷阱。
可以,品种页的 AIGC 模块已内置该类计算,省去手动脚本跑数的过程,但参数适配仍由你定。
结合前篇的基准分布与本条的平均导数窗口,先做样本外回放,再谈实盘概率,外汇贵金属高风险,结论仅作倾向参考。