📏
MQL5 ①HMA(ハル移動平均線)を表示したい
HMAを使ったEAを作ってみたいなと思っています。
ハル移動平均線(Hull Moving Average、HMA)とは
- 遅延とダマシの軽減:従来の移動平均線の弱点である遅延を軽減し、ダマシも少なくするよう設計されています
- 高い価格追従性:HMAは価格変動への追従性が非常に高く、時には価格を追い越すこともあります5。
計算方法:HMAは加重移動平均(WMA)を組み合わせ、一定の係数で均すことで計算されます。具体的には、「n÷2日WMAを2倍したものから、n日WMAを引き、その結果をnの平方根日WMAで均す」という計算式を用います。 - 使用方法:HMAは1本だけの表示で十分効果的です。その傾きを見ることで、相場の方向性を簡単に判断できます。
- 他の移動平均線との違い:同じパラメータの単純移動平均線(SMA)や指数移動平均線(EMA)と比較すると、HMAはローソク足により近い動きをします。
TradingViewで表示したHMA
HMAの計算方法
ハル移動平均線(HMA)の計算方法は以下の手順で行われます:
- 期間nを設定します。
- 期間n/2の加重移動平均(WMA)を計算し、その値を2倍します。
- 期間nの加重移動平均(WMA)を計算します。
- ステップ2の結果からステップ3の結果を引きます。
- ステップ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 を計算する価格データ(例: 終値価格など)。
price
はArrayGetAsSeries
でfalse
を返却するので0が最も古い足、rates_total-1が最新の足。
コード内で設定されるExtLineBuffer
もfalse
を返す。
各部分の解説
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
が少ない(=初回またはバー数が変更された)場合、start
をInpMAPeriod + 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)
-
計算の流れ:
-
start-InpMAPeriod
からstart
の間のバーをループ処理。 - 各バーの価格に重み(
l
= 1, 2, ..., InpMAPeriod)を掛けて加算し、sum
を計算。 - 価格の合計
lsum
と重みの合計weight
を同時に計算。 -
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