美元和欧元指数图表  MetaTrader 5 服务示例·综合运用
📊

美元和欧元指数图表 MetaTrader 5 服务示例·综合运用

(3/3)·把 USDX 与 EURX 的加权几何均值算进 MT5 服务,前两步搭好函数与任务,这回跑通整条链路

案例拆解 第 3/3 篇
不少人直接拿 USDX 当美元强弱的唯一判据,却忽略 1999 年欧元置换后权重已重写。还有人手动抄汇率算 EURX,盘口跳几下数字就废了,回测根本对不上。

「合成货币对的开高低是怎么算出来的」

做篮子型合成品种(比如自定义美元指数)时,单根 M1 K线的开高低不是简单相加,而是按各成分权重做几何加权。核心公式里先放一个 MAIN_COEFF 常数,再对每个成分品种的对应价格取权次幂连乘,外汇与贵金属合成均属高风险,权重配错图形会整体变形。 下面这段是计算函数的主体逻辑:先取各品种 Open 填入 values[],若总 tick_volume 为 0 说明这一分钟没数据直接返 false;随后 open 以 MAIN_COEFF 打底,循环乘上 MathPow(成分价, 权重)。High、Low 同理换取值类型重算一遍。 代码里 BASKET_SIZE 是篮子品种数,ExtWeights[i].weight 是第 i 个品种的权重,GetRateValue 负责按 time 和 VALUE_OPEN/HIGH/LOW 捞出对应价。你在 MT5 里改权重数组后重跑,合成品种的分时波动斜率会明显变化,可用历史数据比对验证。

MQL5 / C++
class="type">bool CalculateRate(class="type">MqlRates& rate,str_rates& symbols_rates[])
  {
   class="type">class="kw">double values[BASKET_SIZE]={class="num">0};
   class="type">long   tick_volume=class="num">0;
   class="type">int    i;
class=class="str">"cmt">//--- get Open prices of all symbols of the instrument basket into the values[] array
   for(i=class="num">0; i<BASKET_SIZE; i++)
      values[i]=GetRateValue(tick_volume,symbols_rates[i],rate.time,VALUE_OPEN);
class=class="str">"cmt">//--- if the tick volume is zero, then there is no data for this minute - class="kw">return &class="macro">#x27;class="kw">false&class="macro">#x27;
   if(tick_volume==class="num">0)
      class="kw">return(class="kw">false);
     
class=class="str">"cmt">//--- write down the total volume of all timeseries
   rate.tick_volume=tick_volume;

class=class="str">"cmt">//--- calculate the Open price based on the prices and weights of all instruments in the basket
   rate.open=MAIN_COEFF;
   for(i=class="num">0; i<BASKET_SIZE; i++)
      rate.open*=MathPow(values[i],ExtWeights[i].weight);
     
class=class="str">"cmt">//--- calculate the High price based on the prices and weights of all instruments in the basket
   for(i=class="num">0; i<BASKET_SIZE; i++)
      values[i]=GetRateValue(tick_volume,symbols_rates[i],rate.time,VALUE_HIGH);
   rate.high=MAIN_COEFF;
   for(i=class="num">0; i<BASKET_SIZE; i++)
      rate.high*=MathPow(values[i],ExtWeights[i].weight);
     
class=class="str">"cmt">//--- calculate the Low price based on the prices and weights of all instruments in the basket
   for(i=class="num">0; i<BASKET_SIZE; i++)
      values[i]=GetRateValue(tick_volume,symbols_rates[i],rate.time,VALUE_LOW);
   rate.low=MAIN_COEFF;
   for(i=class="num">0; i<BASKET_SIZE; i++)
      rate.low*=MathPow(values[i],ExtWeights[i].weight);

◍ 篮子加权合成报价的具体算法

合成一篮子货币(类似美元指数)的收盘价,核心是用各成分货币的权重做幂乘。代码里先把 MAIN_COEFF 作为基底,再对每一种货币对的取值做 MathPow(values[i], weight) 连乘,权重为负代表该货币对与篮子反向。 下面这段循环就是合成 close 的关键:先取每个品种在对应时间的收盘价,再逐一对基底做幂乘累积。 for(i=0; i<BASKET_SIZE; i++) values[i]=GetRateValue(tick_volume,symbols_rates[i],rate.time,VALUE_CLOSE); rate.close=MAIN_COEFF; for(i=0; i<BASKET_SIZE; i++) rate.close*=MathPow(values[i],ExtWeights[i].weight); return(CheckRate(rate)); 美元指数经典公式可直接对照验证:基底常数 50.14348112,EURUSD 权重 -0.576、USDJPY 权重 0.136、GBPUSD 权重 -0.119、USDCAD 权重 0.091、USDSEK 权重 0.042、USDCHF 权重 0.036。把 Open/High/Low/Close 分别代入,就能在 MT5 里复刻出 USDX 四个价位。 GetRateValue 负责按时间定位具体 K 线价格;当请求 VALUE_OPEN 且当前 bar 时间匹配时,会把该品种的 tick_volume 累加进引用变量,用来统计篮子总 tick 量。外汇与贵金属合成品种波动受杠杆与流动性影响,实际报价可能偏离理论值,属高风险验证。

MQL5 / C++
for(i=class="num">0; i<BASKET_SIZE; i++)
  values[i]=GetRateValue(tick_volume,symbols_rates[i],rate.time,VALUE_CLOSE);
rate.close=MAIN_COEFF;
for(i=class="num">0; i<BASKET_SIZE; i++)
  rate.close*=MathPow(values[i],ExtWeights[i].weight);

class="kw">return(CheckRate(rate));
}
Open USDX = class="num">50.14348112 * Open EURUSD^(-class="num">0.576) * Open USDJPY^(class="num">0.136) * Open GBPUSD^(-class="num">0.119) * Open USDCAD^(class="num">0.091) * Open USDSEK^(class="num">0.042) * Open USDCHF^(class="num">0.036);
High USDX = class="num">50.14348112 * High EURUSD^(-class="num">0.576) * High USDJPY^(class="num">0.136) * High GBPUSD^(-class="num">0.119) * High USDCAD^(class="num">0.091) * High USDSEK^(class="num">0.042) * High USDCHF^(class="num">0.036);
Low  USDX = class="num">50.14348112 * Low  EURUSD^(-class="num">0.576) * Low  USDJPY^(class="num">0.136) * Low  GBPUSD^(-class="num">0.119) * Low  USDCAD^(class="num">0.091) * Low  USDSEK^(class="num">0.042) * Low  USDCHF^(class="num">0.036);
CloseUSDX = class="num">50.14348112 * CloseEURUSD^(-class="num">0.576) * CloseUSDJPY^(class="num">0.136) * CloseGBPUSD^(-class="num">0.119) * CloseUSDCAD^(class="num">0.091) * CloseUSDSEK^(class="num">0.042) * CloseUSDCHF^(class="num">0.036);
class="type">class="kw">double GetRateValue(class="type">long &tick_volume,str_rates &symbol_rates,class="type">class="kw">datetime time,ENUM_RATES_VALUES num_value)
  {
   class="type">class="kw">double value=class="num">0;
   class="type">int    index=symbol_rates.index;
   if(index<ArraySize(symbol_rates.rates))
     {
      class="kw">switch(num_value)
        {
         case VALUE_OPEN:
           if(symbol_rates.rates[index].time<time)
             value=symbol_rates.rates[index].close;
           else
            {
             if(symbol_rates.rates[index].time==time)
               {
                value=symbol_rates.rates[index].open;
                tick_volume+=symbol_rates.rates[index].tick_volume;
               }
            }
           class="kw">break;
         case VALUE_HIGH:

最后说句实在话

上面这段取价逻辑里,HIGH 和 LOW 在目标 K 线时间吻合时直接取该根高低点,而早于目标时间的 K 线一律回退到 close,CLOSE 分支则连等于时间也吃 close——这意味着你若用这套函数去回测跨周期拐点,可能把未走完 K 线的瞬时高低误判成定值。 CheckRate 里先把四个价格过一遍 MathIsValidNumber 和零值拦截,再用 ExtDigits 做 NormalizeDouble,随后四条 if 把 high/low 往 open 与 close 之间夹。这套校正能挡掉不少脏数据,但注意它改的是传入引用本身,调用方手里的 MqlRates 会被就地抹平。 外汇和贵金属杠杆高、跳空频繁,这类自行校正若没接在严格的时间边界判断后,可能在极端行情里给出偏离盘口的价格。开 MT5 把 ExtDigits 调成你品种的规范小数位,跑一根非交易时段数据,看 high 是否被 open 强行拉低,就能验证这处细节。

MQL5 / C++
if(symbol_rates.rates[index].time<time)
      value=symbol_rates.rates[index].close;
    else
      {
       if(symbol_rates.rates[index].time==time)
          value=symbol_rates.rates[index].high;
      }
    class="kw">break;
    class=class="str">"cmt">//--- Low price
    case VALUE_LOW:
      if(symbol_rates.rates[index].time<time)
        value=symbol_rates.rates[index].close;
      else
        {
         if(symbol_rates.rates[index].time==time)
            value=symbol_rates.rates[index].low;
        }
      class="kw">break;
    class=class="str">"cmt">//--- Close price
    case VALUE_CLOSE:
      if(symbol_rates.rates[index].time<=time)
        value=symbol_rates.rates[index].close;
      class="kw">break;
      }
    }

class=class="str">"cmt">//--- class="kw">return the received value
  class="kw">return(value);
  }
class=class="str">"cmt">//--- class="kw">return the result of checking prices for validity
  class="kw">return(CheckRate(rate));
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Check prices for validity and class="kw">return the check result            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CheckRate(class="type">MqlRates &rate)
  {
class=class="str">"cmt">//--- if prices are not valid real numbers, or are less than or equal to zero - class="kw">return &class="macro">#x27;class="kw">false&class="macro">#x27;
  if(!MathIsValidNumber(rate.open) || !MathIsValidNumber(rate.high) || !MathIsValidNumber(rate.low) || !MathIsValidNumber(rate.close))
    class="kw">return(class="kw">false);
  if(rate.open<=class="num">0.0 || rate.high<=class="num">0.0 || rate.low<=class="num">0.0 || rate.close<=class="num">0.0)
    class="kw">return(class="kw">false);

class=class="str">"cmt">//--- normalize prices to the required number of digits
  rate.open=NormalizeDouble(rate.open,ExtDigits);
  rate.high=NormalizeDouble(rate.high,ExtDigits);
  rate.low=NormalizeDouble(rate.low,ExtDigits);
  rate.close=NormalizeDouble(rate.close,ExtDigits);

class=class="str">"cmt">//--- adjust prices if necessary
  if(rate.high<rate.open)
    rate.high=rate.open;
  if(rate.low>rate.open)
    rate.low=rate.open;
  if(rate.high<rate.close)
    rate.high=rate.close;
  if(rate.low>rate.close)
    rate.low=rate.close;

class=class="str">"cmt">//--- all is fine
  class="kw">return(true);
  }
把双指数合成交给小布盯盘
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到 USDX 与 EURX 的实时合成曲线,你只管看背离做决策。

常见问题

不一定。100 点是 1973 年基准的加权几何均值,低于它仅表示相对那篮子均值贬值,对单一货币可能分化,概率上更宜看作综合弱势信号。
欧元指数基于五种货币篮子,权重匹配欧洲央行贸易加权口径,其中美国占 31.55%、英国 30.56% 等,并非直接复制美方数据,计算时需注意来源差异。
可以。小布盯盘对应品种页已内置基于 MT5 服务的合成视图,免去了自己写服务程序的麻烦,外汇贵金属波动高风险,先看合成再下手。
几何平均对 multiplicative 的汇率变动更稳健,避免权重被极端单币种扭曲,这也是 USDX 与 EURX 公式的共同基底。