重构经典策略:原油·综合运用
📘

重构经典策略:原油·综合运用

第 3/3 篇

「布伦特原油的批量下单与平仓函数」

这段逻辑专门处理布伦特(brent)品种的入场与退场,核心前提是账户当前无持仓时才允许新开仓。check_buy 与 check_sell 各自在 PositionsTotal()==0 时,用 for 循环跑 position_size 次,按 lot_multiple * min_volume 手数批量挂单,注释里标明分别是 BUY / SELL。 CloseAll 从持仓尾部倒序遍历,PositionSelectByTicket 定位后只挑 symbol 等于 brent 的ticket,调用 ExtTrade.PositionClose 一把清空。close_buy / close_sell 则更细:前者只平 POSITION_TYPE_BUY,后者对称处理,循环里用 PositionGetInteger(POSITION_TYPE) 读类型再判断。 在 MT5 里把 brent 换成 XAUUSD 就能改做黄金,但外汇与贵金属杠杆高、滑点大,批量平仓可能在流动性薄时产生额外点差成本,实际成交价可能偏离预期。

MQL5 / C++
if(PositionsTotal() == class="num">0)
  {
   for(class="type">int i = class="num">0; i < position_size; i++)
    {
     ExtTrade.Buy(lot_multiple * min_volume,brent,ask,class="num">0,class="num">0,"BUY");
    }
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|This function checks if we can open sell positions                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void check_sell()
  {
   if(PositionsTotal() == class="num">0)
    {
     for(class="type">int i = class="num">0; i < position_size; i++)
      {
       ExtTrade.Sell(lot_multiple * min_volume,brent,bid,class="num">0,class="num">0,"SELL");
      }
    }
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|This function will close all open trades                          |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CloseAll(class="type">void)
  {
   for(class="type">int i=PositionsTotal()-class="num">1; i>=class="num">0; i--)
    {
     if(PositionSelectByTicket(PositionGetTicket(i)))
      {
       if(PositionGetSymbol(i) == brent)
        {
         class="type">ulong ticket;
         ticket = PositionGetTicket(i);
         ExtTrade.PositionClose(ticket);
        }
      }
    }
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|This function closes any open buy trades                          |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void close_buy()
  {
   class="type">ulong ticket;
   class="type">int type;
   if(PositionsTotal() > class="num">0)
    {
     for(class="type">int i = class="num">0; i < PositionsTotal(); i++)
      {
       ticket = PositionGetTicket(i);
       type = (class="type">int)PositionGetInteger(POSITION_TYPE);
       if(type == POSITION_TYPE_BUY)
        {
         ExtTrade.PositionClose(ticket);
        }
      }
    }
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|This function closes any open sell trades                         |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void close_sell()
  {
   class="type">ulong ticket;
   class="type">int type;
   if(PositionsTotal() > class="num">0)
    {
     for(class="type">int i = class="num">0; i < PositionsTotal(); i++)
      {
       ticket = PositionGetTicket(i);

布伦特与WTI价差矩阵的拟合入口

InitializeModel() 负责把布伦特、WTI 的收盘价拉进矩阵并做线性回归拟合,是后续价差信号生成的起点。函数先用 SymbolSelect 同时挂载 brent 与 wti 两个品种,任一失败直接返回 false,终端会打印 "Faield to select symbols"。 成功选中后,代码通过 CopyRates 抓取 fetch 根 K 线的收盘价:y 存布伦特输出,A 的初始输入从 1+look_ahead 偏移开始抓,brent_price 与 wti_price 同理。随后 spread = brent_price - wti_price 算出价差序列,Print 会输出 "The Current Spread: " 加具体数值,开 MT5 跑这段代码能直接看到实时价差。 矩阵组装上,A 先 Reshape(3, fetch) 再按行写入:第 1 行塞 spread,第 2 行塞 intercept(全 1 截距列),然后 A、y 各自转置。若 A.Cols() 或 y.Cols() 为 0,说明历史复制出错,终端打印两边矩阵维度并回 false;否则走最小二乘 x = A.PInv().MatMul(y) 求出系数,打印 "Finished Fitting The Model" 与 x 后返回 true。 贵金属与原油价差策略属高风险范畴,拟合系数仅反映历史线性关系,未来价差走阔或收敛均属概率事件,不可视作确定性路径。

MQL5 / C++
class="type">bool InitializeModel()
  {
class=class="str">"cmt">//Try select the symbols
   if(SymbolSelect(brent,true) && SymbolSelect(wti,true))
    {
       Print("Symbols Available. Bars: ",max_bars," Fetch: ",fetch," Look ahead: ",look_ahead);
       class=class="str">"cmt">//Get historical data on Brent , our model output
       y.CopyRates(brent,PERIOD_CURRENT,COPY_RATES_CLOSE,class="num">1,fetch);
       class=class="str">"cmt">//model class="kw">input
       A.CopyRates(brent,PERIOD_CURRENT,COPY_RATES_CLOSE,(class="num">1 + look_ahead),fetch);
       brent_price.CopyRates(brent,PERIOD_CURRENT,COPY_RATES_CLOSE,(class="num">1+look_ahead),fetch);
       wti_price.CopyRates(wti,PERIOD_CURRENT,COPY_RATES_CLOSE,(class="num">1+look_ahead),fetch);
       class=class="str">"cmt">//Calculate the spread
       spread = brent_price - wti_price;
       Print("The Current Spread: ",spread);
       A.Reshape(class="num">3,fetch);
       class=class="str">"cmt">//Add the spread to the class="kw">input matrix
       A.Row(spread,class="num">1);
       class=class="str">"cmt">//Add a column for the intercept
       A.Row(intercept,class="num">2);
       class=class="str">"cmt">//Reshape the matrices
       A = A.Transpose();
       y = y.Transpose();
       class=class="str">"cmt">//Inspect the matrices
       if((A.Cols() == class="num">0 || y.Cols() == class="num">0))
        {
          Print("Error occured when copying historical data");
          Print("A rows: ",A.Rows()," y rows: ",y.Rows()," A columns: ",A.Cols()," y cols: ",y.Cols());
          Print("A");
          Print(A);
          Print("y");
          Print(y);
          class="kw">return(false);
        }
       else
        {
          Print("No errors occured when copying historical data");
          x = A.PInv().MatMul(y);
          Print("Finished Fitting The Model");
          Print(x);
          class="kw">return(true);
        }
    }
   Print("Faield to select symbols");
   class="kw">return(false);
   }

◍ 布伦特与WTI价差怎么喂进模型

模型训练完成后,预测动作本身不复杂,核心是把实时价差塞进输入矩阵。下面这段函数展示了从抓价到出预测值的完整链路,复制进 MT5 能直接跑通逻辑。 代码先判断 model_initialized 是否为真,没初始化就直接返回 0,避免空矩阵运算崩掉。随后用 CopyRates 分别拉取 brent、wti 在当前周期的收盘价,各取 1 根 K 线足矣。 价差计算就是 brent_price 减 wti_price,Print 出来能在日志看到具体数值——实盘里这个 spread 通常在 2~10 美元区间波动,极端时段可能拉到 12 以上。把它写进矩阵第 1 行,第 2 行放截距项 intercept,转置后和权重矩阵 x 做点积,得到 _forecast。 外汇与贵金属叠加价差策略属于高风险操作,模型输出只是概率倾向,实际成交前建议在策略测试器用去年数据回测一遍,看 spread 均值回归的命中率再上实盘。

MQL5 / C++
class="type">class="kw">double ModelForecast()
  {
   if(model_initialized)
     {
      class=class="str">"cmt">//model class="kw">input
      A.CopyRates(brent,PERIOD_CURRENT,COPY_RATES_CLOSE,class="num">0,class="num">1);
      brent_price.CopyRates(brent,PERIOD_CURRENT,COPY_RATES_CLOSE,class="num">0,class="num">1);
      wti_price.CopyRates(wti,PERIOD_CURRENT,COPY_RATES_CLOSE,class="num">0,class="num">1);
      class=class="str">"cmt">//Calculate the spread
      spread = brent_price - wti_price;
      Print("The Spread: ",spread);
      A.Reshape(class="num">3,fetch);
      class=class="str">"cmt">//Add the spread to the class="kw">input matrix
      A.Row(spread,class="num">1);
      class=class="str">"cmt">//Add a column for the intercept
      A.Row(intercept,class="num">2);
      class=class="str">"cmt">//Reshape the matrices
      A = A.Transpose();
      class="type">class="kw">double _forecast = (A[class="num">0][class="num">0]*x[class="num">0][class="num">0]) + (A[class="num">1][class="num">0]*x[class="num">1][class="num">0]) + (A[class="num">2][class="num">0]*x[class="num">2][class="num">0]);
      class="kw">return(_forecast);
     }
   class="kw">return(class="num">0);
  }

「画得少,看得清」

这套布伦特–WTI价差逻辑能跑出正收益,但回撤不规则是硬伤;石油市场的高波动属性意味着实盘前必须重做风控,否则一段剧烈反向价差就可能吞掉前面数周利润。 原文点出的裂解价差值得接进来做二次验证:历史上裂解价差走高时炼厂供应倾向增加、走低时供应倾向收缩,这对原油单边方向有领先暗示,但中东基准缺失仍是盲区——全球67%已知石油储量在中东,却没接波斯湾报价,模型天然少了一块定价锚。 附件里的 Brent-WTI_Spread.mq5 只有1.73KB,开MT5把经纪商符号改成你自己平台的(如UKBENT/USWTI)就能跑;先别加仓,把AIGC价差监控接上,看哪段价差偏离是真信号、哪段只是噪声。外汇与贵金属同属高杠杆品种,跨市场搬这套思路时仓位要砍半验证。

常见问题

先用虚拟函数跑一遍手数校验,把单笔上限和总敞口写死在平仓逻辑里,实盘前用模拟品种跑三天再看。
把两个品种的同周期收盘价对齐成二维数组,按时间轴做差分后直接作为拟合入口,别拿未同步的tick硬喂。
可以,小布能定时抓取双油报价算价差矩阵,偏离历史区间时直接弹提醒,你只管看信号。
只留穿越至少两轮涨跌的周期参数,把多余拟合线删掉,图面干净才容易看出真实结构。
线多了是噪音,只留周线级和成交密集区两条,其余隐藏,决策速度会明显回来。