掉期利率(第一部分):锁定与合成仓位·综合运用
🔁

掉期利率(第一部分):锁定与合成仓位·综合运用

(3/3)·从掉期结构到实用程序,把正掉期思路落进可跑的多账户锁定框架

偏理论进阶 第 3/3 篇
很多老手算过点值却从没算过隔夜掉期占去多少隐性成本。把掉期当噪声忽略,等于主动放弃一类可预测的资金增量。本篇把前两部分的概念拼成能实盘跑的综合用法。

「右侧货币篮子配平的逻辑落点」

这段逻辑承接左侧结算,转去处理等式右半边的货币存量。外层 for 从 ReadStartIterator 扫到 RightSide 字符串长度,遇到 '^' 分隔符或抵达末位时,截取一段子对并读取其方向标记 SubSide。 内层先置 bNew=true,遍历 BasicPairsLeft 数组:若某元素 Value 等于 Half2,则按 SubSide 为 'u' 减库存、为 'd' 加库存,并把 bNew 置 false 后跳出。若全程没匹配到,就找第一个 Value 长度为 0 的空位写入 Half2 并增减 Quantity。 右侧循环每次处理完将 ReadStartIterator 推到 i+1、quantityiterator 自增,直到右串解析完毕。外汇与贵金属合成货币对计算涉及高杠杆与报价跳空,实盘验证前请在 MT5 策略测试器用历史数据跑一遍该段,确认数组越界风险可控。

MQL5 / C++
bNew=true;
for ( class="type">int j=class="num">0; j<ArraySize(BasicPairsLeft); j++ )class=class="str">"cmt">// if the currency is not found in the list, add it
  {
  if ( BasicPairsLeft[j].Value == Half2 )
    {
    if ( SubSide == "u" ) BasicPairsLeft[j].Quantity--;
    if ( SubSide == "d" ) BasicPairsLeft[j].Quantity++;
    bNew = class="kw">false;
    class="kw">break;
    }
  }
if ( bNew )
  {
  for ( class="type">int j=class="num">0; j<ArraySize(BasicPairsLeft); j++ )class=class="str">"cmt">// if the currency is not found in the list, add it
    {
    if ( StringLen(BasicPairsLeft[j].Value) == class="num">0 )
      {
      if ( SubSide == "u" ) BasicPairsLeft[j].Quantity--;
      if ( SubSide == "d" ) BasicPairsLeft[j].Quantity++;
      BasicPairsLeft[j].Value=Half2;
      class="kw">break;
      }
    }
  }
ReadStartIterator=i+class="num">1;
quantityiterator++;

class=class="str">"cmt">/// end of left-side balance calculation

class=class="str">"cmt">//// Calculate balance values for the right side
quantityiterator=class="num">0;
ReadStartIterator=class="num">0;
for ( class="type">int i=ReadStartIterator; i<StringLen(CheckedPair.RightSide); i++ )class=class="str">"cmt">// extract base currencies from the right side of the equation
  {
  Divider=StringSubstr(CheckedPair.RightSide,i,class="num">1);
  if ( Divider == "^"|| i == StringLen(CheckedPair.RightSide) - class="num">1 )
    {
    SubPair=StringSubstr(CheckedPair.RightSide,ReadStartIterator+PrefixE,class="num">6);
    SubSide=StringSubstr(CheckedPair.RightSideStructure,quantityiterator,class="num">1);

从交叉货币对拆出两条腿并记账

这段逻辑干的事很直接:把形如 EURGBP 的交叉对右半边字符串,按固定偏移切出前后各 3 个字符,分别当作基础货币与报价货币去更新一张全局表。Half1 取从 PrefixE 偏移后第 1~3 位,Half2 取紧接着的 3 位,意味着右半边至少得有 6 个字符才拆得干净,否则 StringSubstr 会静默截短。 对外汇、贵金属这类高杠杆品种做币种强度统计时,这种拆法能快速把 28 个主要交叉对归并成 8 个基础币的净持仓偏向;但需注意 MT5 品种名可能因券商后缀(如 m 或 .pro)变长,硬编码 3+3 可能误切。 SubSide 标记报价方向:u 代表该交叉对上涨,则 Half1 计数 +1、Half2 计数 -1;d 则反向。遍历 BasicPairsRight 时一旦匹配到已有 Value 就改数量并置 bNew=false 跳出,没匹配才在第一个空槽写入新币种。 下面这段是原文核心拆表代码,可直接贴进 MT5 脚本里验证拆串与计数走向。

MQL5 / C++
Half1=StringSubstr(CheckedPair.RightSide,ReadStartIterator+PrefixE,class="num">3);
Half2=StringSubstr(CheckedPair.RightSide,ReadStartIterator+PrefixE+class="num">3,class="num">3);
 bNew=true;
 for ( class="type">int j=class="num">0; j<ArraySize(BasicPairsRight); j++ )class=class="str">"cmt">// if the currency is not found in the list, add it
  {
  if ( BasicPairsRight[j].Value == Half1 )
    {
    if ( SubSide == "u" ) BasicPairsRight[j].Quantity++;
    if ( SubSide == "d" ) BasicPairsRight[j].Quantity--;
    bNew = class="kw">false;
    class="kw">break;
    }
  }
 if ( bNew )
  {
  for ( class="type">int j=class="num">0; j<ArraySize(BasicPairsRight); j++ )class=class="str">"cmt">// if the currency is not found in the list, add it
    {
    if ( StringLen(BasicPairsRight[j].Value) == class="num">0 )
      {
      if ( SubSide == "u" ) BasicPairsRight[j].Quantity++;
      if ( SubSide == "d" ) BasicPairsRight[j].Quantity--;
      BasicPairsRight[j].Value=Half1;
      class="kw">break;
      }
    }
  }

 bNew=true;
 for ( class="type">int j=class="num">0; j<ArraySize(BasicPairsRight); j++ )class=class="str">"cmt">// if the currency is not found in the list, add it
  {
  if ( BasicPairsRight[j].Value == Half2 )
    {
    if ( SubSide == "u" ) BasicPairsRight[j].Quantity--;
    if ( SubSide == "d" ) BasicPairsRight[j].Quantity++;
    bNew = class="kw">false;
    class="kw">break;
    }
  }

◍ 左右两侧多空数量的归总与等式校验

右侧配对补位完成后,需要把左右两个数组里每个币种的 Quantity 累加,分别得出左侧上涨总数 LeftUpTotal、左侧下跌总数 LeftDownTotal,以及右侧对应的 RightUpTotal、RightDownTotal。 具体做法是遍历 BasicPairsLeft 与 BasicPairsRight,当 Quantity 大于 0 且 Value 非空时归入上涨计数,小于 0 且 Value 非空时其绝对值归入下跌计数。注意下跌分支用了 -=BasicPairsLeft[i].Quantity,因为 Quantity 本身为负,减完得到正数累计。 校验环节只看一种情况:四方计数必须全部等于 1。也就是说左侧上下各恰有一对、右侧上下也各恰有一对,才认为这个等式结构有效;只要任何一方不是 1,公式就倾向被判为无效,后续逻辑不会进入该分支。外汇与贵金属市场波动剧烈,这类结构判定仅作参考,实盘仍属高风险。 下面这段是右侧补位与左右计数归总的原实现,可直接粘进 MT5 看变量变化:

MQL5 / C++
for ( class="type">int j=class="num">0; j<ArraySize(BasicPairsRight); j++ )class=class="str">"cmt">// if the currency is not found in the list, add it
      {
      if (  StringLen(BasicPairsRight[j].Value) == class="num">0 )
        {
        if ( SubSide == "u" ) BasicPairsRight[j].Quantity--;
        if ( SubSide == "d" ) BasicPairsRight[j].Quantity++;
        BasicPairsRight[j].Value=Half2;
        class="kw">break;
        }
      }
      }

      ReadStartIterator=i+class="num">1;
      quantityiterator++;
      }
   }
   class=class="str">"cmt">/// end of right-side balance calculation
   class=class="str">"cmt">/// calculate the number of lower and upper currencies based on the received data from the previous block
   class="type">int LeftUpTotal=class="num">0;class=class="str">"cmt">// the number of upper elements in the left part
   class="type">int LeftDownTotal=class="num">0;class=class="str">"cmt">// the number of lower elements in the left part
   class="type">int RightUpTotal=class="num">0;class=class="str">"cmt">// the number of upper elements in the right part
   class="type">int RightDownTotal=class="num">0;class=class="str">"cmt">// the number of lower elements in the right part


   class="type">class="kw">string LastUpLeft;
   class="type">class="kw">string LastDownLeft;
   class="type">class="kw">string LastUpRight;
   class="type">class="kw">string LastDownRight;
   for ( class="type">int i=class="num">0; i<ArraySize(BasicPairsLeft); i++ )
    {
    if ( BasicPairsLeft[i].Quantity > class="num">0 && StringLen(BasicPairsLeft[i].Value) > class="num">0 ) LeftUpTotal+=BasicPairsLeft[i].Quantity;
    if ( BasicPairsLeft[i].Quantity < class="num">0 && StringLen(BasicPairsLeft[i].Value) > class="num">0 ) LeftDownTotal-=BasicPairsLeft[i].Quantity;
    }
   for ( class="type">int i=class="num">0; i<ArraySize(BasicPairsRight); i++ )
    {
    if ( BasicPairsRight[i].Quantity > class="num">0 && StringLen(BasicPairsRight[i].Value) > class="num">0 ) RightUpTotal+=BasicPairsRight[i].Quantity;
    if ( BasicPairsRight[i].Quantity < class="num">0 && StringLen(BasicPairsRight[i].Value) > class="num">0 ) RightDownTotal-=BasicPairsRight[i].Quantity;
    }
   class=class="str">"cmt">///
   class=class="str">"cmt">/// check if both sides are equal
   if ( LeftUpTotal == class="num">1 && LeftDownTotal == class="num">1 && RightUpTotal == class="num">1 && RightDownTotal == class="num">1 )class=class="str">"cmt">// there must be one pair in the upper and in the lower part of both sides of the equality, otherwise the formula is invalid
    {

「交叉等价时的右侧结构翻转」

在货币对公式校验逻辑里,左右两侧的基础组合会先各自扫一遍数组,把数量为 +1 且字符串非空的记为 LastUp,数量为 -1 的记为 LastDown。左右两边的 LastUp/LastDown 若完全相等,或交叉相等,才认为分式等价,否则直接返回 false 判公式无效。 当出现交叉相等(Left 的 Up 等于 Right 的 Down,且 Left 的 Down 等于 Right 的 Up)时,相当于右侧整体取了 -1 次方,代码会把 RightSideStructure 里的 'u' 全换成 'd'、'd' 全换成 'u' 生成新结构并回写。这一翻转不改变等式数学含义,只是把表达归一。 若左侧 LastUpLeft 与 LastDownLeft 相同,结果坍缩成 1,公式同样判废。随后用 CorrectedResultInstrument() 把两侧残留拼成 TempResult;当标记 IsResultInstrument 为真且拼接串不等于修正结果时,会再对 RightSideStructure 做一次 u/d 互换,保证结果符号与结构一致。外汇与贵金属波动剧烈,这类结构归一仅用于算式自检,不代表任何方向胜率。

MQL5 / C++
for ( class="type">int i=class="num">0; i<ArraySize(BasicPairsLeft); i++ )
  {
  if ( BasicPairsLeft[i].Quantity == class="num">1 && StringLen(BasicPairsLeft[i].Value) > class="num">0 ) LastUpLeft=BasicPairsLeft[i].Value;
  if ( BasicPairsLeft[i].Quantity == -class="num">1 && StringLen(BasicPairsLeft[i].Value) > class="num">0 ) LastDownLeft=BasicPairsLeft[i].Value;
  }
for ( class="type">int i=class="num">0; i<ArraySize(BasicPairsRight); i++ )
  {
  if ( BasicPairsRight[i].Quantity == class="num">1 && StringLen(BasicPairsRight[i].Value) > class="num">0 ) LastUpRight=BasicPairsRight[i].Value;
  if ( BasicPairsRight[i].Quantity == -class="num">1 && StringLen(BasicPairsRight[i].Value) > class="num">0 ) LastDownRight=BasicPairsRight[i].Value;
  }
  }
 else class="kw">return class="kw">false;
 if ( (LastUpLeft == LastUpRight && LastDownLeft == LastDownRight) || (LastUpLeft == LastDownRight && LastDownLeft == LastUpRight) )
  {
  if ( LastUpLeft == LastDownRight && LastDownLeft == LastUpRight )class=class="str">"cmt">// If the formula is cross-equivalent, then invert the structure of the right part of the equation(it is the same as raising to the power of -class="num">1)
    {
    class="type">class="kw">string NewStructure;class=class="str">"cmt">// the new structure that will be built from the previous one
    for ( class="type">int i=class="num">0; i<StringLen(CheckedPair.RightSideStructure); i++ )
      {
      if ( CheckedPair.RightSideStructure[i] == &class="macro">#x27;u&class="macro">#x27; ) NewStructure+="d";
      if ( CheckedPair.RightSideStructure[i] == &class="macro">#x27;d&class="macro">#x27; ) NewStructure+="u";
      }
    CheckedPair.RightSideStructure=NewStructure;
    }  
  }
 else class="kw">return class="kw">false;class=class="str">"cmt">// if the resulting fractions on both sides are not equivalent, then the formula is invalid
 if ( LastUpLeft == LastDownLeft ) class="kw">return class="kw">false;class=class="str">"cmt">// if result in one, then the formula is invalid
class=class="str">"cmt">/// Now it is necessary to write all the above into a corrected and more convenient structure
 class="type">class="kw">string TempResult=CorrectedResultInstrument(LastUpLeft+LastDownLeft,r.IsResultInstrument);
 if ( r.IsResultInstrument && LastUpLeft+LastDownLeft != TempResult )
  {
  class="type">class="kw">string NewStructure="";class=class="str">"cmt">// the new structure that will be built from the previous one
  for ( class="type">int i=class="num">0; i<StringLen(CheckedPair.RightSideStructure); i++ )
    {
    if ( CheckedPair.RightSideStructure[i] == &class="macro">#x27;u&class="macro">#x27; ) NewStructure+="d";
    if ( CheckedPair.RightSideStructure[i] == &class="macro">#x27;d&class="macro">#x27; ) NewStructure+="u";
    }
  CheckedPair.RightSideStructure=NewStructure;
  NewStructure="";class=class="str">"cmt">// the new structure that will be built from the previous one

左右侧结构翻转与等价符号归并

当左侧结构被判定需要镜像处理时,代码逐字符把 'u' 换成 'd'、'd' 换成 'u',生成 NewStructure 并回写 LeftSideStructure。这一翻转逻辑保证交叉汇率两边的方向语义一致,否则后续归一会得出反向等式。 翻转后,ResultInstrument 由 LastDownLeft+LastUpLeft 或反向拼接得到,同时把 UpPair / DownPair 记录下来。若走 else 分支则保持原顺序,说明当前配对已是标准基准方向。 CorrectedResultInstrument 负责把生成的六位字符串(如 EURUSD)拆成 Half1input 与 Half2input,再遍历 Pairs 数组做直接匹配或交叉匹配。只要 Half1/Half2 与输入两端互换相等,就返回真实可交易符号,bResult 置 true;否则原样返回,bResult 为 false。 下方 EquationCorrected 结构体把左/右侧货币对、结构串、结果符号及上下币种打包,供 bNormalized 做归一化尝试时填充。开 MT5 把 Pairs 数组填上你常看的 28 个直盘交叉盘,跑一遍这个函数就能验证等价符号是否被正确识别。

MQL5 / C++
for ( class="type">int i=class="num">0; i<StringLen(CheckedPair.LeftSideStructure); i++ )
  {
  if ( CheckedPair.LeftSideStructure[i] == &class="macro">#x27;u&class="macro">#x27; ) NewStructure+="d";
  if ( CheckedPair.LeftSideStructure[i] == &class="macro">#x27;d&class="macro">#x27; ) NewStructure+="u";
  }
CheckedPair.LeftSideStructure=NewStructure;      

r.ResultInstrument=LastDownLeft+LastUpLeft;
r.UpPair=LastDownLeft;
r.DownPair=LastUpLeft;      
 }
 else
  {
  r.ResultInstrument=LastUpLeft+LastDownLeft;
  r.UpPair=LastUpLeft;
  r.DownPair=LastDownLeft;
  }
 r.LeftSide=CheckedPair.LeftSide;
 r.RightSide=CheckedPair.RightSide;
 r.LeftSideStructure=CheckedPair.LeftSideStructure;
 r.RightSideStructure=CheckedPair.RightSideStructure;
 class=class="str">"cmt">///  

 class=class="str">"cmt">/// if code has reached this point, it is considered that we have found the formula meeting the criteria, and the next step is normalization

 class="kw">return true;
  }
class="type">class="kw">string CorrectedResultInstrument(class="type">class="kw">string instrument, class="type">bool &bResult)class=class="str">"cmt">// if any equivalent symbol corresponds to the generated formula, class="kw">return this symbol(or leave as is)
  {  
  class="type">class="kw">string Half1="";
  class="type">class="kw">string Half2="";  
  class="type">class="kw">string Half1input=StringSubstr(instrument,class="num">0,class="num">3);class=class="str">"cmt">//class="kw">input upper currency
  class="type">class="kw">string Half2input=StringSubstr(instrument,class="num">3,class="num">3);class=class="str">"cmt">//class="kw">input lower currency
  bResult=class="kw">false;
  for ( class="type">int j=class="num">0; j<ArraySize(Pairs); j++ )
   {
   Half1=StringSubstr(Pairs[j].Name,PrefixE,class="num">3);
   Half2=StringSubstr(Pairs[j].Name,PrefixE+class="num">3,class="num">3);
   if ( (Half1==Half1input && Half2==Half2input) || (Half1==Half2input && Half2==Half1input) )class=class="str">"cmt">// direct match or crossed match
     {
     bResult=true;
     class="kw">return Pairs[j].Name;
     }
   }

  class="kw">return instrument;
  }
class="kw">struct EquationCorrected class=class="str">"cmt">// corrected structure of the basic formula
  {
  class="type">class="kw">string LeftSide;class=class="str">"cmt">// currency pairs participating in the formula on the left side of the "=" sign
  class="type">class="kw">string LeftSideStructure;class=class="str">"cmt">// structure of the left side of the formula
  class="type">class="kw">string RightSide;class=class="str">"cmt">// currency pairs participating in the right side of the formula
  class="type">class="kw">string RightSideStructure;class=class="str">"cmt">// structure of the right side of the formula

  class="type">class="kw">string ResultInstrument;class=class="str">"cmt">// the resulting instrument to which both parts of the formula come after transformation
  class="type">bool IsResultInstrument;class=class="str">"cmt">// has the suitable equivalent symbol been found
  class="type">class="kw">string UpPair;class=class="str">"cmt">// the upper currency of the resulting instrument
  class="type">class="kw">string DownPair;class=class="str">"cmt">// the lower currency of the resulting instrument
  };
class="type">bool bNormalized(EquationCorrected &d,EquationNormalized &v)class=class="str">"cmt">// formula normalization attempt(the normalized formula is returned in "v" )
  {

◍ 正文

&nbsp;&nbsp; <span class="keyword">double</span> PreviousContract;<span class="comment">// previous contract</span> &nbsp;&nbsp; <span class="keyword">bool</span> bWasPairs;<span class="comment">// if any pairs have been found</span> &nbsp;&nbsp; <span class="keyword">double</span> BaseContract;<span class="comment">// contract of the pair to which the equation is reduced</span> &nbsp;&nbsp; <span class="keyword">double</span> PreviousLotK=<span class="number">0.0</span>;<span class="comment">// previous LotK</span> &nbsp;&nbsp; <span class="keyword">double</span> LotK;<span class="comment">// current LotK</span> &nbsp;&nbsp; <span class="keyword">string</span> PreviousSubSide;<span class="comment">// in numerator or denominator (previous factor)</span> &nbsp;&nbsp; <span class="keyword">string</span> PreviousPair;<span class="comment">// previous pair</span> &nbsp;&nbsp; <span class="keyword">string</span> PreviousHalf1;<span class="comment">// upper currency of the previous pair</span> &nbsp;&nbsp; <span class="keyword">string</span> PreviousHalf2;<span class="comment">// lower currency of the previous pair</span> &nbsp;&nbsp; <span class="keyword">string</span> SubPair;<span class="comment">// the full name of the currency pair</span> &nbsp;&nbsp; <span class="keyword">string</span> Half1;<span class="comment">// the first currency of the pair</span> &nbsp;&nbsp; <span class="keyword">string</span> Half2;<span class="comment">// the second currency of the pair</span> &nbsp

「等式左侧的链式配对归一化」

在三角/多腿套利公式解析里,左侧字符串要先拆成若干 6 字符子对(如 EURUSD),再用链式规则判断哪一对能作为入口。代码用 bUsed[] 标记已处理项,tryiterator 控制最大尝试次数,未平衡前会反复扫 LeftSide。 关键判定在 SubSide:'u' 代表该对在分子(买入基准),'d' 代表在分母(卖出基准)。首对要求 Half1 或 Half2 命中 d.UpPair 且方向匹配;后续对则要求前一对的尾货币等于当前对的头货币,否则链会断。 首对手数系数用 LotK=BaseContract/SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE) 计算,以合约规模反推 lot 倍数。外汇与贵金属这类多腿组合波动耦合强,实操前务必在 MT5 策略测试器跑一遍该归一逻辑,确认无遗漏配对。

MQL5 / C++
class="type">bool bBalanced=class="kw">false;class=class="str">"cmt">// is the formula balanced
class="type">bool bUsed[];
ArrayResize(bUsed,tryiterator);
ArrayFill(bUsed,class="num">0,tryiterator,class="kw">false);
class="type">int balancediterator=class="num">0;
PreviousHalf1="";
PreviousHalf2="";
PreviousLotK=class="num">0.0;
PreviousSubSide="";
PreviousPair="";
PreviousContract=class="num">0.0;
bWasPairs=class="kw">false;class=class="str">"cmt">// have there been pairs
for ( class="type">int k=class="num">0;k<tryiterator; k++ )class=class="str">"cmt">// try to normalize the left side
    {
    if( !bBalanced )
      {
      quantityiterator=class="num">0;
      ReadStartIterator=class="num">0;
      for ( class="type">int i=ReadStartIterator; i<StringLen(d.LeftSide); i++ )class=class="str">"cmt">// extract base currencies from the left side of the equation
        {
        Divider=StringSubstr(d.LeftSide,i,class="num">1);
        if ( Divider == "^" || i == StringLen(d.LeftSide) - class="num">1 )
          {
          SubPair=StringSubstr(d.LeftSide,ReadStartIterator+PrefixE,class="num">6);
          SubSide=StringSubstr(d.LeftSideStructure,quantityiterator,class="num">1);
          Half1=StringSubstr(d.LeftSide,ReadStartIterator+PrefixE,class="num">3);
          Half2=StringSubstr(d.LeftSide,ReadStartIterator+PrefixE+class="num">3,class="num">3);
          if ( ! bUsed[quantityiterator] && (( PreviousHalf1 == "" && ((Half1 == d.UpPair && SubSide == "u") || (Half2 == d.UpPair && SubSide == "d")) ) class=class="str">"cmt">// if it is the first pair in the list
          || ( (( PreviousHalf2 == Half1 && PreviousSubSide == "u" ) || ( PreviousHalf1 == Half1 && PreviousSubSide == "d" )) && SubSide == "u" ) class=class="str">"cmt">// if the current pair is in the numerator
          || ( (( PreviousHalf2 == Half2 && PreviousSubSide == "u" ) || ( PreviousHalf1 == Half2 && PreviousSubSide == "d" )) && SubSide == "d" )) )class=class="str">"cmt">// if the current pair is in the denominator
            {class=class="str">"cmt">// find the entry point(pair) of the chain
            if( PreviousHalf1 == "" )class=class="str">"cmt">// define the lot coefficient of the first pair
              {
              if ( SubSide == "u" )
                {
                LotK=BaseContract/SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE);class=class="str">"cmt">// (class="num">1 start)

跨品种手数系数的链式推导

配对篮子加仓时,手数系数 LotK 不是拍脑袋定的,而是按合约规模和实时报价一层层算出来。基础货币对用 BaseContract 除以子品种合约规模再除以卖价(SubSide=="d" 时取 BID)得到首层 LotK,并把 PreviousLotK、PreviousContract 缓存下来供后续品种复用。 当上一腿是 "u" 且当前腿也是 "u",LotK 直接继承上一腿系数并按价格与合约比缩放:LotK = PreviousLotK * Pp * (PreviousContract / 当前合约规模)。这里 Pp 是上一品种的 BID,若返回 0 或上一合约规模 ≤0 会直接 return false 中断,避免脏数据进场。 外汇与贵金属跨品种对冲属高风险操作,报价或合约规模取数失败会令系数失真,实盘前应在 MT5 用 Print() 把每段 LotK 打出来核对。

MQL5 / C++
v.PairLeft[balancediterator].LotK=LotK;
PreviousLotK=LotK;
bWasPairs=true;
PreviousContract=SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE);
 }
 if ( SubSide == "d" )
 {
 class="type">class="kw">double Pt=SymbolInfoDouble(SubPair,SYMBOL_BID);
 if ( Pt == class="num">0.0 ) class="kw">return class="kw">false;
 LotK=(BaseContract/SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE))/Pt;class=class="str">"cmt">// (class="num">2 start)
 v.PairLeft[balancediterator].LotK=LotK;
 PreviousLotK=LotK;
 bWasPairs=true;
 PreviousContract=SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE);
 } 
 }
 else
 {
 if( PreviousSubSide == "u" )class=class="str">"cmt">// define the lot coefficient of further pairs
 {
 if ( SubSide == "u" )
 {
 class="type">class="kw">double Pp=SymbolInfoDouble(PreviousPair,SYMBOL_BID);
 if ( Pp == class="num">0.0 ) class="kw">return class="kw">false;
 if ( PreviousContract <= class="num">0.0 ) class="kw">return class="kw">false;
 LotK=PreviousLotK*Pp*(PreviousContract/SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE));class=class="str">"cmt">// ( class="num">1 )
 v.PairLeft[balancediterator].LotK=LotK;

◍ 跨品种手数系数的递推算法

在配对 basket 里,不同品种合约规模(contract size)和当前报价不同,直接复制手数会让名义价值失衡。这段逻辑用 LotK 作为手数换算系数,把上一腿的暴露按比例传递到下一腿。 当上一腿是做多(u)、当前腿也是做多时,LotK 只按合约规模比修正:PreviousLotK * (PreviousContract / 当前品种合约规模)。若上一腿做多、当前腿做空(d),则额外乘入 Bid 价格比 Pp/Pt,用公式 (2) 把多空两边的点值对齐。 反过来上一腿做空、当前腿做多时,公式 (3) 退化为仅合约规模比,因为空腿已经用 Bid 价格做过暴露折算。所有分支都先判 SymbolInfoDouble 返回 0.0 或合约规模 ≤0.0 直接 return false,避免除零和脏数据污染后续 PairLeft[].LotK。 开 MT5 把这段塞进你的 basket 初始化函数,打印每个 PairLeft[i].LotK,能直接看到黄金(合约 100)和欧美(合约 100000)之间系数差出 1000 倍级别。外汇与贵金属杠杆高,系数算错可能瞬间放大风险敞口。

MQL5 / C++
PreviousLotK=LotK;
PreviousContract=SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE);
}
if ( SubSide == "d" )
  {
  class="type">class="kw">double Pt=SymbolInfoDouble(SubPair,SYMBOL_BID);
  class="type">class="kw">double Pp=SymbolInfoDouble(PreviousPair,SYMBOL_BID);
  if ( Pt == class="num">0.0 ) class="kw">return class="kw">false;
  if ( Pp == class="num">0.0 ) class="kw">return class="kw">false;
  if ( PreviousContract <= class="num">0.0 ) class="kw">return class="kw">false;
  LotK=PreviousLotK*(Pp/Pt)*(PreviousContract/SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE));class=class="str">"cmt">// ( class="num">2 )
  v.PairLeft[balancediterator].LotK=LotK;
  PreviousLotK=LotK;
  PreviousContract=SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE);
  }
if( PreviousSubSide == "d" )class=class="str">"cmt">// define the lot coefficient of further pairs
  {
  if ( SubSide == "u" )
    {
    if ( PreviousContract <= class="num">0.0 ) class="kw">return class="kw">false;
    LotK=PreviousLotK*(PreviousContract/SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE));class=class="str">"cmt">// ( class="num">3 )
    v.PairLeft[balancediterator].LotK=LotK;
    PreviousLotK=LotK;

「子对反向下单时的手数换算」

当子货币对处于卖价侧(SubSide == "d")时,不能直接沿用上一腿的 LotK,必须按合约规模与实时卖价重新折算。代码里先取 SYMBOL_BID,若返回 0.0 或上一腿合约规模小于等于 0.0 则直接 return false,避免除零与脏数据扩散。 折算核心在 LotK=(PreviousLotK/Pt)*(PreviousContract/SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE))。也就是用旧手数除以当前卖价,再乘「旧合约规模 / 新合约规模」的比值;外汇与贵金属杠杆品种合约规模可能不同,忽略这步会让对冲腿名义价值偏离。 算出的 LotK 写回 v.PairLeft[balancediterator].LotK,并把 PreviousLotK 与 PreviousContract 刷新为当前值,供下一轮循环使用。开 MT5 把这段接进你的平衡逻辑,改 SubPair 合约规模看 LotK 跳变就能验证。 另一段是基础货币清单的归并:遍历 BasicPairsLeft,若 Half1 已存在就按 SubSide 是 "u" 加数量、"d" 减数量,并置 bNew=false 跳出;不在表里才走后续新增分支。

MQL5 / C++
PreviousContract=SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE);
}
if ( SubSide == "d" )
  {
   class="type">class="kw">double Pt=SymbolInfoDouble(SubPair,SYMBOL_BID);
   if ( Pt == class="num">0.0 ) class="kw">return class="kw">false;
   if ( PreviousContract <= class="num">0.0 ) class="kw">return class="kw">false;
   LotK=(PreviousLotK/Pt)*(PreviousContract/SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE));class=class="str">"cmt">// ( class="num">4 )
   v.PairLeft[balancediterator].LotK=LotK;
   PreviousLotK=LotK;
   PreviousContract=SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE);
  }
}
         
bNew=true;
for ( class="type">int j=class="num">0; j<ArraySize(BasicPairsLeft); j++ )class=class="str">"cmt">// if the currency is not found in the list, add it
  {
   if ( BasicPairsLeft[j].Value == Half1 )
     {
      if ( SubSide == "u" ) BasicPairsLeft[j].Quantity++;
      if ( SubSide == "d" ) BasicPairsLeft[j].Quantity--;
      bNew = class="kw">false;
      class="kw">break;
     }
  }
if ( bNew )
  {

用数组登记货币对的双向增减

这段逻辑在维护一个叫 BasicPairsLeft 的结构体数组,用来记录每种基础货币在当前篮子里的净持仓方向。Half1 与 Half2 是两个待配对的货币代号,SubSide 用 "u" 和 "d" 标记子腿的上下方向。 第一个循环遍历整个数组,若某槽位的 Value 长度为 0(即空槽),就把 Half1 写进去,并按 SubSide 给 Quantity 加一或减一,随后 break 退出。这里净量 +1 表示偏向多头腿,-1 表示偏空头腿,外汇与贵金属篮子组合里这种符号直接决定对冲权重,属于高风险操作。 紧接着设 bNew=true,再跑第二个循环找 Half2 是否已在数组中。命中则反向调整 Quantity(u 减 d 加),并把 bNew 置 false 跳出;若全程没命中,第三个循环又会去捞空槽塞入 Half2。 [CODE] for ( int j=0; j<ArraySize(BasicPairsLeft); j++ )// if the currency is not found in the list, add it { if ( StringLen(BasicPairsLeft[j].Value) == 0 ) { if ( SubSide == "u" ) BasicPairsLeft[j].Quantity++; if ( SubSide == "d" ) BasicPairsLeft[j].Quantity--; BasicPairsLeft[j].Value=Half1; break; } } bNew=true; for ( int j=0; j<ArraySize(BasicPairsLeft); j++ )// if the currency is not found in the list, add it { if ( BasicPairsLeft[j].Value == Half2 ) { if ( SubSide == "u" ) BasicPairsLeft[j].Quantity--; if ( SubSide == "d" ) BasicPairsLeft[j].Quantity++; bNew = false; break; } } if ( bNew ) { for ( int j=0; j<ArraySize(BasicPairsLeft); j++ )// if the currency is not found in the list, add it { if ( StringLen(BasicPairsLeft[j].Value) == 0 ) { [/CODE] 逐行拆解:第 1 行定义 j 从 0 跑到数组长度减一,注释说找不到就加;第 3 行用 StringLen 判空槽;第 5、6 行按 SubSide 改 Quantity;第 7 行写入 Half1;第 8 行 break 防重复写。bNew=true 之后第二个循环第 14 行比对 Value 是否等于 Half2,命中就反向改量并置 false,说明 Half2 已存在时不扩数组。最后 bNew 仍真才进第三个循环捞空槽,这段在 MT5 里直接贴进 EA 的篮子构建函数就能跑,建议用 Print 打出每次 Quantity 变化验证配对倾向。

MQL5 / C++
for ( class="type">int j=class="num">0; j<ArraySize(BasicPairsLeft); j++ )class=class="str">"cmt">// if the currency is not found in the list, add it
  {
  if ( StringLen(BasicPairsLeft[j].Value) == class="num">0 )
    {
    if ( SubSide == "u" ) BasicPairsLeft[j].Quantity++;
    if ( SubSide == "d" ) BasicPairsLeft[j].Quantity--;
    BasicPairsLeft[j].Value=Half1;
    class="kw">break;
    }
  }

bNew=true;
for ( class="type">int j=class="num">0; j<ArraySize(BasicPairsLeft); j++ )class=class="str">"cmt">// if the currency is not found in the list, add it
  {
  if ( BasicPairsLeft[j].Value == Half2 )
    {
    if ( SubSide == "u" ) BasicPairsLeft[j].Quantity--;
    if ( SubSide == "d" ) BasicPairsLeft[j].Quantity++;
    bNew = class="kw">false;
    class="kw">break;
    }
  }
if ( bNew )
  {
  for ( class="type">int j=class="num">0; j<ArraySize(BasicPairsLeft); j++ )class=class="str">"cmt">// if the currency is not found in the list, add it
    {
    if (  StringLen(BasicPairsLeft[j].Value) == class="num">0 )
      {

◍ 配对平衡时的退出与计数逻辑

这段片段处理三角套利或货币对合成时,左半边基础池的增减与退出判定。当子边标记为 "u"(分子方向)时对 BasicPairsLeft[j] 的 Quantity 减一,标记为 "d"(分母方向)时加一,并把该槽位的 Value 强制设为 Half2 后 break,说明单步配对一旦命中就终止当前内层扫描。 随后把已配对的子对写进 v.PairLeft 数组:记录名称、方向,并用 SymbolInfoDouble 取 SYMBOL_TRADE_CONTRACT_SIZE 填合约规模。balancediterator 自增,把本次的 Half1、Half2、SubSide、SubPair 存进 Previous 系列变量,quantityleft 也加一,供下一轮差分使用。 平衡判定的核心是方向与非缺失半边的对应关系:若 SubSide=="u" 且 Half2 等于 d.DownPair(分数未倒置),或 SubSide=="d" 且 Half1 等于 d.DownPair(分数倒置),就把 bBalanced 置 true 并 break——意味着公式左右已配平,剩余组合不再需要。 循环末尾初始化 LeftUpTotal 与 LeftDownTotal 两个整型计数器,分别统计左部尚余的分子、分母元素数量。开 MT5 把这段嵌进你的合成对扫描函数,打印 quantityleft 与 bBalanced 的触发频次,就能看出配对收敛在哪一类倒置结构上。外汇与贵金属合成套利含高杠杆风险,回测结论仅代表历史结构概率。

MQL5 / C++
if ( SubSide == "u" ) BasicPairsLeft[j].Quantity--;
if ( SubSide == "d" ) BasicPairsLeft[j].Quantity++;
BasicPairsLeft[j].Value=Half2;
class="kw">break;
}
}

v.PairLeft[balancediterator].Name=SubPair;
v.PairLeft[balancediterator].Side=SubSide;
v.PairLeft[balancediterator].ContractSize=SymbolInfoDouble(v.PairLeft[balancediterator].Name, SYMBOL_TRADE_CONTRACT_SIZE);

balancediterator++;
PreviousHalf1=Half1;
PreviousHalf2=Half2;
PreviousSubSide=SubSide;
PreviousPair=SubPair;

quantityleft++;
if ( SubSide == "u" && Half2 == d.DownPair )class=class="str">"cmt">// if the fraction is not inverted
  {
   bBalanced=true;class=class="str">"cmt">// if the missing part is in the denominator, then we have balanced the formula
   class="kw">break;class=class="str">"cmt">// since the formula is balanced, we don&class="macro">#x27;t need the rest
  }
if ( SubSide == "d" && Half1 == d.DownPair )class=class="str">"cmt">// if the fraction is inverted
  {
   bBalanced=true;class=class="str">"cmt">// if the missing part is in the numerator, then we have balanced the formula
   class="kw">break;class=class="str">"cmt">// since the formula is balanced, we don&class="macro">#x27;t need the rest
  }

class="type">int LeftUpTotal=class="num">0;class=class="str">"cmt">// the number of upper elements in the left part
class="type">int LeftDownTotal=class="num">0;class=class="str">"cmt">// the number of lower elements in the left part

「右侧货币对提取与平衡标记初始化」

左侧若未平衡,代码直接 return false,右侧不再计算——这是三角套息公式解析里的硬短路,能省掉一半无效遍历。 右侧先用一个 for 循环扫 d.RightSide 字符串,碰到 '^' 分隔符或走到末位(i == StringLen(d.RightSide)-1)就 tryiterator 自增,本质是数出右半边有多少个基础货币对单元。 数完立刻 ArrayResize(v.PairRight, tryiterator) 给右侧结构数组定容。随后把 bBalanced 重置为 false,ArrayResize(bUsed, tryiterator) 配 ArrayFill 全填 false,表示右侧各单元尚未参与平衡。 balancediterator、PreviousHalf1/2、PreviousLotK、PreviousSubSide、PreviousPair、PreviousContract 等全部清零或置空,bWasPairs 也回到 false,为下一轮右侧配对循环铺干净状态。外汇与贵金属算法套利高风险,这类平衡状态判断错误可能直接漏掉可行组合。

MQL5 / C++
class="type">class="kw">string LastUpLeft;
class="type">class="kw">string LastDownLeft;
for ( class="type">int z=class="num">0; z<ArraySize(BasicPairsLeft); z++ )
  {
  if ( BasicPairsLeft[z].Quantity > class="num">0 && StringLen(BasicPairsLeft[z].Value) > class="num">0 ) LeftUpTotal+=BasicPairsLeft[z].Quantity;
  if ( BasicPairsLeft[z].Quantity < class="num">0 && StringLen(BasicPairsLeft[z].Value) > class="num">0 ) LeftDownTotal-=BasicPairsLeft[z].Quantity;
  }
if ( bWasPairs && LeftUpTotal == class="num">0 && LeftDownTotal == class="num">0 ) class="kw">return class="kw">false;
  }
ReadStartIterator=i+class="num">1;
bUsed[quantityiterator]=true;
quantityiterator++;
  }
  }
 else class="kw">break;
 }
 class=class="str">"cmt">/// end of coefficient calculation for the left part

if ( !bBalanced ) class="kw">return class="kw">false;class=class="str">"cmt">// if the left side is not balanced, then there is no point in balancing the right side

class=class="str">"cmt">//// Calculate the number of pairs for the right side
tryiterator=class="num">0;
ReadStartIterator=class="num">0;
for ( class="type">int i=ReadStartIterator; i<StringLen(d.RightSide); i++ )class=class="str">"cmt">// extract base currencies from the right side of the equation
  {
  Divider=StringSubstr(d.RightSide,i,class="num">1);
  if ( Divider == "^" )
    {
    ReadStartIterator=i+class="num">1;
    tryiterator++;
    }

  if ( i == StringLen(d.RightSide) - class="num">1 )
    {
    ReadStartIterator=i+class="num">1;
    tryiterator++;
    }    
  }  
 ArrayResize(v.PairRight,tryiterator);
 class=class="str">"cmt">/// end of calculation of the number of pairs for the right side

bBalanced=class="kw">false;class=class="str">"cmt">// is the formula balanced
ArrayResize(bUsed,tryiterator);
ArrayFill(bUsed,class="num">0,tryiterator,class="kw">false);
balancediterator=class="num">0;
PreviousHalf1="";
PreviousHalf2="";
PreviousLotK=class="num">0.0;
PreviousSubSide="";
PreviousPair="";
PreviousContract=class="num">0.0;
bWasPairs=class="kw">false;

右侧货币链的头寸系数推导

在三角套利的符号方程里,右侧字符串代表待平仓或待开仓的货币对序列。引擎用一层 for(k) 循环反复尝试归一化右侧,直到 bBalanced 置位,这意味着单遍扫描可能漏掉链路起点,需要最多 tryiterator 次回扫。 内层逻辑从 ReadStartIterator 起逐字符读 RightSide,遇到 '^' 分隔符或抵达末尾时,截取 6 字符子对(SubPair)与结构标记 SubSide('u' 表分子、'd' 表分母)。若 bUsed 未占用且首尾衔接条件满足,便锁定该子对为链条入口。 首对的手数系数由 BaseContract 除以合约规模得出:LotK = BaseContract / SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE)。例如 BaseContract 为 100000、XAUUSD 合约规模 100,则首对 LotK = 1000,后续 PreviousLotK 与 PreviousContract 被记录供链式传递。 外汇与贵金属跨式组合波动剧烈,合约规模读错会直接放大敞口,实盘前务必在 MT5 用 SymbolInfoDouble 核对每个 SubPair 的 SYMBOL_TRADE_CONTRACT_SIZE。

MQL5 / C++
for ( class="type">int k=class="num">0; k<tryiterator; k++ )class=class="str">"cmt">// try to normalize the right side
      {
      if ( !bBalanced )
        {
        quantityiterator=class="num">0;
        ReadStartIterator=class="num">0;
        for ( class="type">int i=ReadStartIterator; i<StringLen(d.RightSide); i++ )class=class="str">"cmt">// extract base currencies from the right side of the equation
          {
          Divider=StringSubstr(d.RightSide,i,class="num">1);
          if ( Divider == "^" || i == StringLen(d.RightSide) - class="num">1 )
            {
            SubPair=StringSubstr(d.RightSide,ReadStartIterator+PrefixE,class="num">6);
            SubSide=StringSubstr(d.RightSideStructure,quantityiterator,class="num">1);
            Half1=StringSubstr(d.RightSide,ReadStartIterator+PrefixE,class="num">3);
            Half2=StringSubstr(d.RightSide,ReadStartIterator+PrefixE+class="num">3,class="num">3);
            
            if ( ! bUsed[quantityiterator] && (( PreviousHalf1 == "" && ((Half1 == d.UpPair && SubSide == "u") || (Half2 == d.UpPair && SubSide == "d")) ) class=class="str">"cmt">// if it is the first pair in the list
            || ( (( PreviousHalf2 == Half1 && PreviousSubSide == "u" ) || ( PreviousHalf1 == Half1 && PreviousSubSide == "d" )) && SubSide == "u" ) class=class="str">"cmt">// if the current pair is in the numerator
            || ( (( PreviousHalf2 == Half2 && PreviousSubSide == "u" ) || ( PreviousHalf1 == Half2 && PreviousSubSide == "d" )) && SubSide == "d" )) )class=class="str">"cmt">// if the current pair is in the denominator
              {class=class="str">"cmt">// find the entry point(pair) of the chain
              if( PreviousHalf1 == "" )class=class="str">"cmt">// define the lot coefficient of the first pair
                {
                if ( SubSide == "u" )
                  {
                  LotK=BaseContract/SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE);class=class="str">"cmt">// (class="num">1 start)
                  v.PairRight[balancediterator].LotK=LotK;
                  PreviousLotK=LotK;
                  bWasPairs=true;
                  PreviousContract=SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE);
                  }

◍ 跨品种手数系数的链式推导

做多腿(SubSide=="d")是整条对冲链的锚点:先取交易品种的 BID 报价 Pt,若返回 0.0 直接 return false 避免脏数据。手数系数按 BaseContract 除以合约规模再除以 Pt 计算,等价于把基准名义金额折算成该品种所需手数比例。 后续若延续做多(上一腿与当前腿都是 "u"),则用手数系数乘以上一腿 BID 价 Pp,再乘合约规模比进行传递,公式见代码段 (1)。这里强制校验 Pp 与 PreviousContract 非零,否则中断——外汇与贵金属跨品种组合中,任一品种行情中断都可能让系数失真,杠杆错配风险偏高。 下面这段是 MT5 里实际跑的系数赋值核心,注意 SYMBOL_TRADE_CONTRACT_SIZE 在不同品种间差异极大(黄金 100、部分外汇 100000),直接决定 LotK 量级。

MQL5 / C++
if ( SubSide == "d" )
              {
              class="type">class="kw">double Pt=SymbolInfoDouble(SubPair,SYMBOL_BID);
              if ( Pt == class="num">0.0 ) class="kw">return class="kw">false;
              LotK=(BaseContract/SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE))/Pt;class=class="str">"cmt">// (class="num">2 start)
              v.PairRight[balancediterator].LotK=LotK;
              PreviousLotK=LotK;
              bWasPairs=true;
              PreviousContract=SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE);
               }
             }
             else
              {
              if( PreviousSubSide == "u" )class=class="str">"cmt">// define the lot coefficient of further pairs
                {
                if ( SubSide == "u" )
                 {
                  class="type">class="kw">double Pp=SymbolInfoDouble(PreviousPair,SYMBOL_BID);
                  if ( Pp == class="num">0.0 ) class="kw">return class="kw">false;
                  if ( PreviousContract <= class="num">0.0 ) class="kw">return class="kw">false;
                  LotK=PreviousLotK*Pp*(PreviousContract/SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE));class=class="str">"cmt">// (class="num">1)
                  v.PairRight[balancediterator].LotK=LotK;
                  PreviousLotK=LotK;
                  PreviousContract=SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE);
                 }
                if ( SubSide == "d" )

「跨品种手数系数的递推算法」

做多腿转做多腿时,手数系数不能简单照搬,得按合约规模和报价比例重算。下面这段逻辑就是处理 SubPair 相对 PreviousPair 的 LotK 递推,外汇与贵金属跨品种组合里合约大小差异大,忽略这步会直接扭曲风险敞口。 核心公式在第 (2) 行:LotK = PreviousLotK * (Pp/Pt) * (PreviousContract / 当前合约大小)。其中 Pt、Pp 分别是子品种和前品种的 BID 报价,若任一取不到(等于 0.0)或前合约小于等于 0.0,函数直接返回 false,避免脏数据往下传。 当上一腿方向标记为 'd' 且当前为反向 'u' 时,公式简化为第 (3) 行:LotK = PreviousLotK * (PreviousContract / 当前合约大小),不再乘报价比。这是因为方向反转场景下,仅按合约规模归一化就能保持名义价值对等,具体效果可在 MT5 策略测试器里挂两组品种验证。 跨品种套利本身受点差和滑点影响,贵金属跳空时报价可能为 0,实盘前务必用 Print 把每一步 LotK 打出来核对。外汇与贵金属杠杆高,错算一手系数就可能放大数倍亏损,属典型高风险操作。

MQL5 / C++
{
 class="type">class="kw">double Pt=SymbolInfoDouble(SubPair,SYMBOL_BID);
 class="type">class="kw">double Pp=SymbolInfoDouble(PreviousPair,SYMBOL_BID);
 if ( Pt == class="num">0.0 ) class="kw">return class="kw">false;
 if ( Pp == class="num">0.0 ) class="kw">return class="kw">false;
 if ( PreviousContract <= class="num">0.0 ) class="kw">return class="kw">false;
 LotK=PreviousLotK*(Pp/Pt)*(PreviousContract/SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE));class=class="str">"cmt">// (class="num">2)
 v.PairRight[balancediterator].LotK=LotK;
 PreviousLotK=LotK;
 PreviousContract=SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE);
 }
 }
 if( PreviousSubSide == "d" )class=class="str">"cmt">// define the lot coefficient of further pairs
 {
 if ( SubSide == "u" )
  {
  if ( PreviousContract <= class="num">0.0 ) class="kw">return class="kw">false;
  LotK=PreviousLotK*(PreviousContract/SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE));class=class="str">"cmt">// (class="num">3)
  v.PairRight[balancediterator].LotK=LotK;
  PreviousLotK=LotK;
  PreviousContract=SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE);
  }
 if ( SubSide == "d" )
  {

子品种手数系数的跨合约换算

跨品种对冲里最容易被忽略的是合约规模不一致:EURUSD 和 XAUUSD 每手对应的基础资产量完全不同,直接用旧系数乘价格会歪。下面这段逻辑在子品种 BID 为 0 或上一合约规模为 0 时直接返回,避免除零和脏数据进数组。 double Pt=SymbolInfoDouble(SubPair,SYMBOL_BID); if ( Pt == 0.0 ) return false; if ( PreviousContract <= 0.0 ) return false; LotK=(PreviousLotK/Pt)*(PreviousContract/SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE)); 逐行看:第一行取子品种当前买价;第二、三行做防御,买价或上一合约规模非法就退出;第四行是核心,用「旧手数系数 ÷ 现买价 ×(旧合约规模 ÷ 新合约规模)」把系数重标定到当前子品种。后面把算出的 LotK 写回数组并滚动更新 PreviousLotK 与 PreviousContract,供下一轮使用。 另一块是基础货币方向的累加:遍历 BasicPairsRight,若 Half1 已存在就按子边方向 u/d 对 Quantity 加一或减一,并置 bNew=false 跳出。若全程没匹配到,才进左侧空位写入。外汇与贵金属跨品种组合高风险,系数算错可能让敞口偏向单边,建议开 MT5 用「品种规格」窗口核对 SYMBOL_TRADE_CONTRACT_SIZE 再跑。

MQL5 / C++
class="type">class="kw">double Pt=SymbolInfoDouble(SubPair,SYMBOL_BID);
if ( Pt == class="num">0.0 ) class="kw">return class="kw">false;
if ( PreviousContract <= class="num">0.0 ) class="kw">return class="kw">false;
LotK=(PreviousLotK/Pt)*(PreviousContract/SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE));class=class="str">"cmt">// (class="num">4)
v.PairRight[balancediterator].LotK=LotK;
PreviousLotK=LotK;
PreviousContract=SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE);
}
}
}
bNew=true;
for ( class="type">int j=class="num">0; j<ArraySize(BasicPairsRight); j++ )class=class="str">"cmt">// if the currency is not found in the list, add it
  {
   if ( BasicPairsRight[j].Value == Half1 )
     {
      if ( SubSide == "u" ) BasicPairsRight[j].Quantity++;
      if ( SubSide == "d" ) BasicPairsRight[j].Quantity--;
      bNew = class="kw">false;
      class="kw">break;
     }
  }
if ( bNew )
  {
   for ( class="type">int j=class="num">0; j<ArraySize(BasicPairsLeft); j++ )class=class="str">"cmt">// if the currency is not found in the list, add it
     {
      if ( StringLen(BasicPairsLeft[j].Value) == class="num">0 )
        {

◍ 右侧货币篮的增减仓逻辑

这段片段处理的是右侧基础货币数组 BasicPairsRight 的双向调整:当子腿方向 SubSide 标记为 "u"(上行)时对应货币的数量字段加一,标记为 "d"(下行)时减一,并把该格的 Value 锚定到 Half1 或 Half2 半价变量,随后 break 退出扫描。 若第一轮没在数组里命中 Value == Half2 的格子,就置 bNew=true 并再跑一遍,找到则反向改 Quantity(u 减 d 加)并把 bNew 复位为 false,说明是存量更新而非新建。 仍没匹配就扫空槽(StringLen(Value)==0),往里写 Half2 与对应正负数量,完成追加。外汇与贵金属组合统计带高杠杆风险,这类篮子计数只反映结构偏移,不预示单边行情。

MQL5 / C++
if ( SubSide == "u" ) BasicPairsRight[j].Quantity++;
if ( SubSide == "d" ) BasicPairsRight[j].Quantity--;
BasicPairsRight[j].Value=Half1;
class="kw">break;
}
}
}
bNew=true;
for ( class="type">int j=class="num">0; j<ArraySize(BasicPairsRight); j++ )class=class="str">"cmt">// if the currency is not found in the list, add it
  {
  if ( BasicPairsRight[j].Value == Half2 )
    {
    if ( SubSide == "u" ) BasicPairsRight[j].Quantity--;
    if ( SubSide == "d" ) BasicPairsRight[j].Quantity++;
    bNew = class="kw">false;
    class="kw">break;
    }
  }
if ( bNew )
  {
  for ( class="type">int j=class="num">0; j<ArraySize(BasicPairsRight); j++ )class=class="str">"cmt">// if the currency is not found in the list, add it
    {
    if ( StringLen(BasicPairsRight[j].Value) == class="num">0 )
      {
      if ( SubSide == "u" ) BasicPairsRight[j].Quantity--;
      if ( SubSide == "d" ) BasicPairsRight[j].Quantity++;
      BasicPairsRight[j].Value=Half2;
      class="kw">break;

「配对结构收尾与右侧组分统计」

这段逻辑处在三角套利配对拆分的收口位置:把拆出的子对写入右侧数组,并立刻用 SymbolInfoDouble 拉取合约规格。 v.PairRight[balancediterator].ContractSize=SymbolInfoDouble(v.PairRight[balancediterator].Name, SYMBOL_TRADE_CONTRACT_SIZE); 这一步很关键,贵金属与外汇跨品种配对时合约乘数差异大,不实时取就会让后续手数折算失真。 随后用 SubSide 与 Half 的位置关系判断公式是否已平衡:非倒置时分母补齐即 break,倒置时分子补齐即 break,平衡后多余组合直接丢弃。 循环外另起 RightUpTotal / RightDownTotal 两个计数器与 LastUpRight / LastDownRight 两个字符串,为下一步统计右侧分子分母元素数量留接口,当前片段里 for(z=0;z<ArraySize(BasicPairsRight);z++) 才刚开头。

MQL5 / C++
v.PairRight[balancediterator].Name=SubPair;
v.PairRight[balancediterator].Side=SubSide;
v.PairRight[balancediterator].ContractSize=SymbolInfoDouble(v.PairRight[balancediterator].Name, SYMBOL_TRADE_CONTRACT_SIZE);
balancediterator++;
PreviousHalf1=Half1;
PreviousHalf2=Half2;
PreviousSubSide=SubSide;
PreviousPair=SubPair;

quantityright++;
if ( SubSide == "u" && Half2 == d.DownPair )class=class="str">"cmt">// if the fraction is not inverted
  {
   bBalanced=true;class=class="str">"cmt">// if the missing part is in the denominator, then we have balanced the formula
   class="kw">break;class=class="str">"cmt">// since the formula is balanced, we don&class="macro">#x27;t need the rest
   }
if ( SubSide == "d" && Half1 == d.DownPair )class=class="str">"cmt">// if the fraction is inverted
  {
   bBalanced=true;class=class="str">"cmt">// if the missing part is in the numerator, then we have balanced the formula
   class="kw">break;class=class="str">"cmt">// since the formula is balanced, we don&class="macro">#x27;t need the rest
   }

class="type">int RightUpTotal=class="num">0;class=class="str">"cmt">// the number of upper elements in the right part
class="type">int RightDownTotal=class="num">0;class=class="str">"cmt">// the number of lower elements in the right part
class="type">class="kw">string LastUpRight;
class="type">class="kw">string LastDownRight;

for ( class="type">int z=class="num">0; z<ArraySize(BasicPairsRight); z++ )
  {

三角套利的归一化与掉期因子计算

在三角套利方程的归一化处理里,右侧多空手数要分别累加:当某基础货币对数量为正数且字符串非空时,RightUpTotal 加该数量;数量为负数时,RightDownTotal 减去该负值(即累加绝对值)。若标记 bWasPairs 为真但两侧总量都为零,函数直接返回 false,说明这组合并不成立。 归一化后有个硬约束:若 quantityleft 与 quantityright 都等于 1,意味着方程被压缩到仅剩两个货币对,代码注释明确写「at least 3 pairs are required」,此时返回 false——少于三个对的「套利」在数学上只是直接兑换,没有闭环价差可捕。外汇与贵金属市场高杠杆、滑点随机,这类结构失效很常见。 最终的 EquationNormalized 结构体把左右边货币对数组、正负掉期相对值及综合 SwapFactor 都装了进去。CalculateBestVariation 会先初始化直向与反向的 SwapPlus / SwapMinus 为 0.0,再遍历 PairLeft 去 Pairs 全局数组里按 Name 匹配,把 Margin、TickValue、SwapBuy、SwapSell 补全。这一步是后续算最优掉期因子的数据基座,开 MT5 把这段接进 EA,打印 ii.PairLeft[i].SwapBuy 就能验证品种属性有没有抓错。

MQL5 / C++
if ( BasicPairsRight[z].Quantity > class="num">0 && StringLen(BasicPairsRight[z].Value) > class="num">0 ) RightUpTotal+=BasicPairsRight[z].Quantity;
if ( BasicPairsRight[z].Quantity < class="num">0 && StringLen(BasicPairsRight[z].Value) > class="num">0 ) RightDownTotal-=BasicPairsRight[z].Quantity;
}
if ( bWasPairs && RightUpTotal == class="num">0 && RightDownTotal == class="num">0 ) class="kw">return class="kw">false;
}
ReadStartIterator=i+class="num">1;
bUsed[quantityiterator]=true;
quantityiterator++;
}
}
}
else class="kw">break;
}

if ( quantityleft == class="num">1 && quantityright == class="num">1 ) class="kw">return class="kw">false;class=class="str">"cmt">// if the equation has been normalized to only class="num">2 pairs, it is not valid(at least class="num">3 pairs are required)

class="kw">return bBalanced;
}
class="kw">struct EquationNormalized class=class="str">"cmt">// the final structure with the formula in normalized form
  {
  Pair PairLeft[];class=class="str">"cmt">// currency pairs on the left side
  Pair PairRight[];class=class="str">"cmt">// currency pairs on the right side
  class="type">class="kw">double SwapPlusRelative;class=class="str">"cmt">// relative equivalent of the positive swap
  class="type">class="kw">double SwapMinusRelative;class=class="str">"cmt">// relative equivalent of the negative swap
  class="type">class="kw">double SwapFactor;class=class="str">"cmt">// resulting swap factor
  };
class="type">void CalculateBestVariation(EquationNormalized &ii)class=class="str">"cmt">// calculation of the best swap factor of the formula and final structure adjustment if needed
  {
  class="type">class="kw">double SwapMinus=class="num">0.0;class=class="str">"cmt">// total negative swap
  class="type">class="kw">double SwapPlus=class="num">0.0;class=class="str">"cmt">// total positive swap
  class="type">class="kw">double SwapMinusReverse=class="num">0.0;class=class="str">"cmt">// total negative swap
  class="type">class="kw">double SwapPlusReverse=class="num">0.0;class=class="str">"cmt">// total positive swap

  class="type">class="kw">double SwapFactor=class="num">0.0;class=class="str">"cmt">// swap factor of the direct pass
  class="type">class="kw">double SwapFactorReverse=class="num">0.0;class=class="str">"cmt">// swap factor of the reverse pass

  for ( class="type">int i=class="num">0; i<ArraySize(ii.PairLeft); i++ )class=class="str">"cmt">// define the missing parameters for calculating the left side
    {
    for ( class="type">int j=class="num">0; j<ArraySize(Pairs); j++ )
      {
      if ( Pairs[j].Name == ii.PairLeft[i].Name )
        {
        ii.PairLeft[i].Margin=Pairs[j].Margin;
        ii.PairLeft[i].TickValue=Pairs[j].TickValue;
        ii.PairLeft[i].SwapBuy=Pairs[j].SwapBuy;
        ii.PairLeft[i].SwapSell=Pairs[j].SwapSell;
        class="kw">break;

◍ 价差组合里隔夜利息的分账算法

合成货币对拆成左右两条腿之后,右腿那几个品种的保证金、点值、多空掉期都还是空的,得从全市场品种池里按名称捞回来填进结构体,否则后面算总持仓成本会直接漏项。 外层循环扫 ii.PairRight,内层拿 Pairs 数组逐个比 Name,命中就把 Margin、TickValue、SwapBuy、SwapSell 抄过去然后 break,避免重复匹配浪费 CPU。 掉期汇总分正向腿和反向腿两套变量:SwapPlus / SwapMinus 记实际方向,SwapPlusReverse / SwapMinusReverse 记反手情形。左腿 Side 标 "u" 时多单吃 SwapBuy、空单吃 SwapSell;标 "d" 时反过来,右腿 "d" 的映射又与左腿 "u" 对称,这套镜像关系写错一个符号,回测里的隔夜成本就会整体偏移。 开 MT5 把这段塞进你自己的价差计算器,跑一组 EUR/USD 与 GBP/USD 的腿,对比 SwapPlus 与券商终端显示的合计掉期,偏差超过 1 个点值就得查 LotK 系数有没有乘错。外汇与贵金属杠杆品种隔夜成本随持仓日变动,高风险,数字仅作结构验证用。

MQL5 / C++
for ( class="type">int i=class="num">0; i<ArraySize(ii.PairRight); i++ )class=class="str">"cmt">// define the missing parameters for calculating the right side
  {
  for ( class="type">int j=class="num">0; j<ArraySize(Pairs); j++ )
    {
    if ( Pairs[j].Name == ii.PairRight[i].Name )
      {
      ii.PairRight[i].Margin=Pairs[j].Margin;
      ii.PairRight[i].TickValue=Pairs[j].TickValue;
      ii.PairRight[i].SwapBuy=Pairs[j].SwapBuy;
      ii.PairRight[i].SwapSell=Pairs[j].SwapSell;
      class="kw">break;
      }
    }
  }

class="type">class="kw">double TempSwap;
class=class="str">"cmt">// calculate all components taking into account a change in the structure
for ( class="type">int i=class="num">0; i<ArraySize(ii.PairLeft); i++ )class=class="str">"cmt">// for left parts
  {
  if ( ii.PairLeft[i].Side == "u" )
    {class=class="str">"cmt">// for direct trading
    TempSwap=ii.PairLeft[i].SwapBuy*ii.LotKLeft[i];
    if ( TempSwap >= class="num">0 ) SwapPlus+=TempSwap;
    else SwapMinus-=TempSwap;
    class=class="str">"cmt">// for reverse trading
    TempSwap=ii.PairLeft[i].SwapSell*ii.LotKLeft[i];
    if ( TempSwap >= class="num">0 ) SwapPlusReverse+=TempSwap;
    else SwapMinusReverse-=TempSwap;      
    }
  if ( ii.PairLeft[i].Side == "d" )
    {class=class="str">"cmt">// for direct trading
    TempSwap=ii.PairLeft[i].SwapSell*ii.LotKLeft[i];
    if ( TempSwap >= class="num">0 ) SwapPlus+=TempSwap;
    else SwapMinus-=TempSwap;
    class=class="str">"cmt">// for reverse trading
    TempSwap=ii.PairLeft[i].SwapBuy*ii.LotKLeft[i];
    if ( TempSwap >= class="num">0 ) SwapPlusReverse+=TempSwap;
    else SwapMinusReverse-=TempSwap;      
    }
  }

for ( class="type">int i=class="num">0; i<ArraySize(ii.PairRight); i++ )class=class="str">"cmt">// for right parts
  {
  if ( ii.PairRight[i].Side == "d" )
    {class=class="str">"cmt">// for direct trading
    TempSwap=ii.PairRight[i].SwapBuy*ii.LotKRight[i];
    if ( TempSwap >= class="num">0 ) SwapPlus+=TempSwap;
    else SwapMinus-=TempSwap;
    class=class="str">"cmt">// for reverse trading
    TempSwap=ii.PairRight[i].SwapSell*ii.LotKRight[i];
    if ( TempSwap >= class="num">0 ) SwapPlusReverse+=TempSwap;
    else SwapMinusReverse-=TempSwap;      
    }

「掉期因子双向测算与结构翻转」

配对交易里掉期成本会吃掉利润,所以代码对正向和反向两套路径分别累加 SwapPlus 与 SwapMinus。正向取右侧结构里 Side=="u" 的卖单掉期乘以手数系数,反向则用同一位置的买单掉期,正负分别归入 Plus 和 Minus 容器。 掉期因子 SwapFactor 用 Plus/Minus 的比值表达:两侧皆正时为正常比率;皆零给 1.0;只正不给负给 1000001.0(视为无限有利);只负不给正则 0.0(完全不利)。反向路径 SwapFactorReverse 套用同一套四分支判定。 比较两者后保留较大者进 ii.SwapFactor。若反向占优,除了写入 Reverse 系列的 Plus/Minus,还要把 PairRight 与 PairLeft 里每项的 Side 做 u↔d 互换——这段循环用 bSigned 锁单次翻转,避免一个元素被改两次。外汇与贵金属杠杆高,掉期因子仅是持仓成本维度之一,实际过夜风险可能随点差扩大而突变。 开 MT5 把这段粘进 EA 初始化后打印 ii.SwapFactor,切换测试品种观察 1000001.0 与 0.0 两种边界什么时候被触发,能直观看到平台掉期表如何左右路径选择。

MQL5 / C++
if ( ii.PairRight[i].Side == "u" )
     {class=class="str">"cmt">// for direct trading
     TempSwap=ii.PairRight[i].SwapSell*ii.LotKRight[i];
     if ( TempSwap >= class="num">0 ) SwapPlus+=TempSwap;
     else SwapMinus-=TempSwap;
     class=class="str">"cmt">// for reverse trading
     TempSwap=ii.PairRight[i].SwapBuy*ii.LotKRight[i];
     if ( TempSwap >= class="num">0 ) SwapPlusReverse+=TempSwap;
     else SwapMinusReverse-=TempSwap;       
     }
  }
  class=class="str">"cmt">// calculate the swap factor for the direct pass
  if ( SwapMinus > class="num">0.0 && SwapPlus > class="num">0.0 ) SwapFactor=SwapPlus/SwapMinus;
  if ( SwapMinus == class="num">0.0 && SwapPlus == class="num">0.0 ) SwapFactor=class="num">1.0;
  if ( SwapMinus == class="num">0.0 && SwapPlus > class="num">0.0 ) SwapFactor=class="num">1000001.0;
  if ( SwapMinus > class="num">0.0 && SwapPlus == class="num">0.0 ) SwapFactor=class="num">0.0;
  class=class="str">"cmt">// calculate the swap factor for the reverse pass
  if ( SwapMinusReverse > class="num">0.0 && SwapPlusReverse > class="num">0.0 ) SwapFactorReverse=SwapPlusReverse/SwapMinusReverse;
  if ( SwapMinusReverse == class="num">0.0 && SwapPlusReverse == class="num">0.0 ) SwapFactorReverse=class="num">1.0;
  if ( SwapMinusReverse == class="num">0.0 && SwapPlusReverse > class="num">0.0 ) SwapFactorReverse=class="num">1000001.0;
  if ( SwapMinusReverse > class="num">0.0 && SwapPlusReverse == class="num">0.0 ) SwapFactorReverse=class="num">0.0;
  class=class="str">"cmt">// select the best approach and calculate the missing values in the structure
  if ( SwapFactor > SwapFactorReverse )
    {
    ii.SwapPlusRelative=SwapPlus;
    ii.SwapMinusRelative=SwapMinus;
    ii.SwapFactor=SwapFactor;
    }
  else
    {
    ii.SwapPlusRelative=SwapPlusReverse;
    ii.SwapMinusRelative=SwapMinusReverse;
    ii.SwapFactor=SwapFactorReverse;
    class="type">bool bSigned;
    for ( class="type">int i=class="num">0; i<ArraySize(ii.PairRight); i++ )class=class="str">"cmt">// if it is a reverse pass, then reverse the right structure of the formula
      {
      bSigned=class="kw">false;
      if ( !bSigned && ii.PairRight[i].Side == "u" )
        {
        ii.PairRight[i].Side="d";
        bSigned=true;
        }
      if ( !bSigned && ii.PairRight[i].Side == "d" )
        {
        ii.PairRight[i].Side="u";
        bSigned=true;
        }
      }
    bSigned=class="kw">false;  
    for ( class="type">int i=class="num">0; i<ArraySize(ii.PairLeft); i++ )class=class="str">"cmt">// if it is a reverse pass, then reverse the left structure of the formula
      {
      bSigned=class="kw">false;

左右两侧方向标记的反转逻辑

在配对结构里,左侧和右侧的涨跌标记需要按规则翻转,否则后续合成波形会错位。左侧仅在未签名状态下把 u 翻成 d、d 翻成 u,且每个元素只处理一次。 右侧则无条件反转:遍历 PairRight 数组,对每个元素做 u↔d 互换,注释直接写明 reverse the right side anyway。 下面这段 MQL5 是实际落点,左侧翻转被包在更外层的判断里,右侧是独立 for 循环。外汇与贵金属波动剧烈,这类标记错误可能在回测中放大为假信号,建议开 MT5 把数组打印出来核对。

MQL5 / C++
  if ( !bSigned && ii.PairLeft[i].Side == "u" )
    {
    ii.PairLeft[i].Side="d";
    bSigned=true;
    }
  if ( !bSigned && ii.PairLeft[i].Side == "d" )
    {
    ii.PairLeft[i].Side="u";
    bSigned=true;
    }
    }

  class="type">bool bSigned;
  for ( class="type">int i=class="num">0; i<ArraySize(ii.PairRight); i++ )class=class="str">"cmt">// reverse the right side anyway
  {
  bSigned=class="kw">false;
  if ( !bSigned && ii.PairRight[i].Side == "u" )
    {
    ii.PairRight[i].Side="d";
    bSigned=true;
    }
  if ( !bSigned && ii.PairRight[i].Side == "d" )
    {
    ii.PairRight[i].Side="u";
    bSigned=true;
    }
  }

◍ 首轮原型跑出的掉期结论

这套复杂原型很难一步给足定论,但首轮实操已经扒出几条能落地的线索。截至目前,我还没在任何货币对上抓到大于 1 的掉期利率因子,这点必须如实说。 对部分货币对,你可以通过合成等价仓位,把正掉期利率往上推,或者把负掉期利率往下压。即便没凑出整条盈利回路,回路里的某一段也总能拿来在两个账户之间做互逆锁定。 这种锁定有意思的地方在于:两端都能吃到正掉期,等于绕开了无掉期账户的需求。外汇与贵金属本身杠杆高、点差滑点难测,这类结构只适合先开 MT5 用策略测试器验证,别直接上实盘。 后续得挑几家主流经纪商做更深扫描,当前功能还太单薄。若执行得当,掉期可能贡献少量但偏稳的利润——目前盈利因子仍只是猜测,未证实。

「在经纪商掉期表里找缝隙」

两笔等量互逆持仓若只靠价格波动,总掉期大概率是净负的——没有哪家经纪商单向正掉期能盖过逆向负掉期,数学上双向同正也不可能。但锁定后整体掉期并非死亏,靠扫描掉期表里的非对称模组,有可能拿到总盈利掉期,构成不依赖价格方向的微利来源。外汇与贵金属杠杆高,这类策略仍受点差和展期规则突变影响,实盘前务必在模拟盘验证。 文中 FillPairsArray 就是扫 MarketWatch 品种、按掉期模式过滤可用标的的入口。下面拆关键片段: void FillPairsArray() // 在数组中填入所需的仪器信息 int iterator=0; // 数组写入游标 double correction; // 掉期修正系数 int TempSwapMode; // 临时存放掉期计算模式 for(int i=0;i<ArraySize(Pairs);i++) // 先清空旧名称 Pairs[iterator].Name=""; for(int i=0;i<SymbolsTotal(false);i++) // 遍历窗口内全部符号 TempSwapMode=int(SymbolInfoInteger(Pairs[iterator].Name,SYMBOL_SWAP_MODE)); // 取掉期模式

if(StringLen(SymbolName(i,false))==6+PrefixE+PostfixE && IsValid(...) && SymbolInfoInteger(...,SYMBOL_TRADE_MODE)==SYMBOL_TRADE_MODE_FULL && ((TempSwapMode==1)(...))) // 名称长度、可交易、掉期模式合规才收

if(iterator>=ArraySize(Pairs)) break; // 数组满就停 Pairs[iterator].Name=SymbolName(i,false); // 记入标的 Pairs[iterator].TickSize=SymbolInfoDouble(...,SYMBOL_TRADE_TICK_SIZE); // 每跳价值 Pairs[iterator].PointX=SymbolInfoDouble(...,SYMBOL_POINT); // 最小变动点 Pairs[iterator].ContractSize=SymbolInfoDouble(...,SYMBOL_TRADE_CONTRACT_SIZE); // 合约规模 switch(TempSwapMode){ case 1: // 以点计掉期 下一篇会接着改这段代码、加利润预测,思路是先跑通扫描再谈锁仓配比。

MQL5 / C++
class="type">void FillPairsArray()class=class="str">"cmt">// 在数组中填入所需的仪器信息
  {
  class="type">int iterator=class="num">0;
  class="type">class="kw">double correction;
  class="type">int TempSwapMode;

  for ( class="type">int i=class="num">0; i<ArraySize(Pairs); i++ )class=class="str">"cmt">// 重置符号
    {
    Pairs[iterator].Name="";
    }  

  for ( class="type">int i=class="num">0; i<SymbolsTotal(class="kw">false); i++ )class=class="str">"cmt">// 从 MarketWatch 窗口检查符号
    {
    TempSwapMode=class="type">int(SymbolInfoInteger(Pairs[iterator].Name,SYMBOL_SWAP_MODE));
    if ( StringLen(SymbolName(i,class="kw">false)) == class="num">6+PrefixE+PostfixE && IsValid(SymbolName(i,class="kw">false)) && SymbolInfoInteger(SymbolName(i,class="kw">false),SYMBOL_TRADE_MODE) == SYMBOL_TRADE_MODE_FULL  
    && ( ( TempSwapMode  == class="num">1 )  ||  ( ( TempSwapMode == class="num">5 || TempSwapMode == class="num">6 ) && CorrectedValue(Pairs[iterator].Name,correction) )) )
      {
      if ( iterator >= ArraySize(Pairs) ) class="kw">break;
      Pairs[iterator].Name=SymbolName(i,class="kw">false);
      Pairs[iterator].TickSize=SymbolInfoDouble(Pairs[iterator].Name,SYMBOL_TRADE_TICK_SIZE);
      Pairs[iterator].PointX=SymbolInfoDouble(Pairs[iterator].Name, SYMBOL_POINT);
      Pairs[iterator].ContractSize=SymbolInfoDouble(Pairs[iterator].Name,SYMBOL_TRADE_CONTRACT_SIZE);
      class="kw">switch(TempSwapMode)
        {
        case  class="num">1:class=class="str">"cmt">// 以点为单位

把掉期换算塞进品种结构里

隔夜利息的计算不能只认一种报价单位。上面这段逻辑按经纪商返回的 SYMBOL_SWAP_MODE 分支处理:模式 4 直接用点值乘掉期点数,再乘上 PointX/TickSize 的合约比例;模式 5 和 6 则是百分比报价,用 correction 系数乘买价、合约规模,再除以 360.0*100.0 摊到每日。 注意 SYMBOL_TRADE_TICK_VALUE 在循环末尾才补取一次,前面模式 4 的换算已经依赖它,所以实际跑之前得确认 TickValue 在 switch 前已被赋值,否则多品种扫描时掉期买价会算成 0。 外汇与贵金属杠杆品种的高风险在于:同一平台不同品种掉期模式可能混用,肉眼看不出,靠这套分支才能把多品种持仓成本拉平比较。

MQL5 / C++
            Pairs[iterator].SwapBuy=SymbolInfoDouble(Pairs[iterator].Name,SYMBOL_SWAP_LONG)*Pairs[iterator].TickValue*(Pairs[iterator].PointX/Pairs[iterator].TickSize);
            Pairs[iterator].SwapSell=SymbolInfoDouble(Pairs[iterator].Name,SYMBOL_SWAP_SHORT)*Pairs[iterator].TickValue*(Pairs[iterator].PointX/Pairs[iterator].TickSize);
            class="kw">break;
            case  class="num">5:class=class="str">"cmt">// 以百分比为单位
            Pairs[iterator].SwapBuy=correction*SymbolInfoDouble(Pairs[iterator].Name,SYMBOL_SWAP_LONG)*SymbolInfoDouble(Pairs[iterator].Name,SYMBOL_BID)*Pairs[iterator].ContractSize/(class="num">360.0*class="num">100.0);
            Pairs[iterator].SwapSell=correction*SymbolInfoDouble(Pairs[iterator].Name,SYMBOL_SWAP_SHORT)*SymbolInfoDouble(Pairs[iterator].Name,SYMBOL_BID)*Pairs[iterator].ContractSize/(class="num">360.0*class="num">100.0);
            class="kw">break;
            case  class="num">6:class=class="str">"cmt">// 以百分比为单位
            Pairs[iterator].SwapBuy=correction*SymbolInfoDouble(Pairs[iterator].Name,SYMBOL_SWAP_LONG)*SymbolInfoDouble(Pairs[iterator].Name,SYMBOL_BID)*Pairs[iterator].ContractSize/(class="num">360.0*class="num">100.0);
            Pairs[iterator].SwapSell=correction*SymbolInfoDouble(Pairs[iterator].Name,SYMBOL_SWAP_SHORT)*SymbolInfoDouble(Pairs[iterator].Name,SYMBOL_BID)*Pairs[iterator].ContractSize/(class="num">360.0*class="num">100.0);
            class="kw">break;
            }
            Pairs[iterator].Margin=SymbolInfoDouble(Pairs[iterator].Name,SYMBOL_MARGIN_INITIAL);
            Pairs[iterator].TickValue=SymbolInfoDouble(Pairs[iterator].Name,SYMBOL_TRADE_TICK_VALUE); class=class="str">"cmt">// <= this
            iterator++;
            }
      }
把多账户对账交给小布
双账户互锁后的掉期净值与汇率偏移,小布盯盘的 AIGC 已内置核对视图,打开对应品种页就能直接看两侧持仓的滚动掉期差,你只管判断锁仓结构要不要调。

常见问题

多数经纪商在服务器日切时刻按自身时区计收,周三转周四统一乘三;跨平台要对齐各自规范里的 server time,不能拿本地时钟推算。
合成仓通过相关品种组合逼近中性,掉期分布被打散到不同标的,和同标的对锁的净掉期结构不同,需逐腿重算。
跨账户涉及币种转换,汇率跳空会让两边权益出现偏差,概率上倾向用低波动交叉对并留缓冲权益。
可以,品种页的掉期面板会标出当日及三倍夜预估,转负或异常放大时给出提示,省去手动翻规范。
优先核对汇率采样频率和掉期读取时点,这两处偏差在回测里最常掩盖真实锁仓收益。