📏

MQL5 ①HMA(ハル移動平均線)を表示したい

2024/12/14に公開

HMAを使ったEAを作ってみたいなと思っています。

ハル移動平均線(Hull Moving Average、HMA)とは

  1. 遅延とダマシの軽減:従来の移動平均線の弱点である遅延を軽減し、ダマシも少なくするよう設計されています
  2. 高い価格追従性:HMAは価格変動への追従性が非常に高く、時には価格を追い越すこともあります5。
    計算方法:HMAは加重移動平均(WMA)を組み合わせ、一定の係数で均すことで計算されます。具体的には、「n÷2日WMAを2倍したものから、n日WMAを引き、その結果をnの平方根日WMAで均す」という計算式を用います。
  3. 使用方法:HMAは1本だけの表示で十分効果的です。その傾きを見ることで、相場の方向性を簡単に判断できます。
  4. 他の移動平均線との違い:同じパラメータの単純移動平均線(SMA)や指数移動平均線(EMA)と比較すると、HMAはローソク足により近い動きをします。

    TradingViewで表示したHMA

HMAの計算方法

ハル移動平均線(HMA)の計算方法は以下の手順で行われます:

  1. 期間nを設定します。
  2. 期間n/2の加重移動平均(WMA)を計算し、その値を2倍します。
  3. 期間nの加重移動平均(WMA)を計算します。
  4. ステップ2の結果からステップ3の結果を引きます。
  5. ステップ4で得られた値を使って、期間√n(nの平方根)の加重移動平均を計算します。

これを数式で表すと以下のようになります:
HMA = WMA(2 * WMA(n/2) - WMA(n), √n)
ここで、WMA(x, y)はxの値をy期間で加重移動平均を取ることを意味します

WMAから計算されるみたいなのでまずはWMAからですね

WMAにはすでに答えがあります。
MetaTraderの中にはすでにLWMAが標準装備されていて、そのコードを確認することができます。
データフォルダ>MQL5>Indicater>Examples フォルダの中に以下のファイルがあるのでこれを開きましょう。
Custom Moving Average.mq5

この中にLWMAを計算する関数があります。(LWMAとWMAは同じ)

void CalculateLWMA(int rates_total,int prev_calculated,int begin,const double &price[])
  {
   int    weight=0;
   int    i,l,start;
   double sum=0.0,lsum=0.0;
//--- first calculation or number of bars was changed
   if(prev_calculated<=InpMAPeriod+begin+2)
     {
      start=InpMAPeriod+begin;
      //--- set empty value for first start bars
      for(i=0; i<start; i++)
         ExtLineBuffer[i]=0.0;
     }
   else
      start=prev_calculated-1;

   for(i=start-InpMAPeriod,l=1; i<start; i++,l++)
     {
      sum   +=price[i]*l;
      lsum  +=price[i];
      weight+=l;
     }
   ExtLineBuffer[start-1]=sum/weight;
//--- main loop
   for(i=start; i<rates_total && !IsStopped(); i++)
     {
      sum             =sum-lsum+price[i]*InpMAPeriod;
      lsum            =lsum-price[i-InpMAPeriod]+price[i];
      ExtLineBuffer[i]=sum/weight;
     }
  }

LWMA関数

このコードは、MQL5 のテクニカルインジケーターで使われる 線形加重移動平均(LWMA) を計算する関数です。以下に、このコードがどのように動作するのか、分解して説明します。


全体の目的

この関数は price[] 配列に与えられた価格データを基に、LWMA を計算し、ExtLineBuffer に結果を格納します。

  • rates_total: 総バー数(価格データの本数)。
  • prev_calculated: 以前の計算で処理したバー数。
  • begin: データの開始位置(バッファの先頭オフセット)。
  • price[]: LWMA を計算する価格データ(例: 終値価格など)。

priceArrayGetAsSeriesfalseを返却するので0が最も古い足、rates_total-1が最新の足。
コード内で設定されるExtLineBufferfalseを返す。

各部分の解説

1. 初期設定

if(prev_calculated<=InpMAPeriod+begin+2)
{
   start=InpMAPeriod+begin;
   for(i=0; i<start; i++)
      ExtLineBuffer[i]=0.0;
}
else
   start=prev_calculated-1;
  • 初回計算か、バー数が変更された場合の処理:
    • prev_calculated が少ない(=初回またはバー数が変更された)場合、startInpMAPeriod + begin に設定。
    • ExtLineBuffer の初期部分(計算前のバー)は値がないため、0.0 をセットします。
  • 以前の計算がある場合の処理:
    • 前回計算した最後のバー(prev_calculated-1)から再計算します。

2. 初期の LWMA 計算

for(i=start-InpMAPeriod,l=1; i<start; i++,l++)
{
   sum   +=price[i]*l;
   lsum  +=price[i];
   weight+=l;
}
ExtLineBuffer[start-1]=sum/weight;
  • LWMA は以下の公式に従います:

    LWMA = 加重価格の合計(sum)÷ 重みの合計(weight)

  • 計算の流れ:

    1. start-InpMAPeriod から start の間のバーをループ処理。
    2. 各バーの価格に重み(l = 1, 2, ..., InpMAPeriod)を掛けて加算し、sum を計算。
    3. 価格の合計 lsum と重みの合計 weight を同時に計算。
    4. start-1 の位置に最初の LWMA を格納。

3. メインループで LWMA を効率的に計算

for(i=start; i<rates_total && !IsStopped(); i++)
{
   sum             =sum-lsum+price[i]*InpMAPeriod;
   lsum            =lsum-price[i-InpMAPeriod]+price[i];
   ExtLineBuffer[i]=sum/weight;
}
  • 効率化のための工夫:

    • LWMA を毎回すべて再計算せず、前回の計算結果を利用して効率化。
    • sum 更新:
      • 古いデータ(price[i-InpMAPeriod])を引き、新しいデータ(price[i]*InpMAPeriod)を追加。
    • lsum 更新:
      • 古いデータを引き、新しいデータを加算。
    • この仕組みにより、各バーにおける LWMA を高速に更新可能。
  • 停止チェック:

    • IsStopped() を用いて計算の中断を確認します(負荷軽減や緊急停止時)。

新しい足がrates_total-1の方がすべての足を再計算しなくていいから、
時系列じゃない方が早そう。

Discussion