コンテンツにスキップ

【検証#14】インジケータが重い?「全期間再計算」の罪

全期間計算の結果

図1:全期間計算モード(激重)の結果

差分更新の結果

図2:差分更新モード(高速)の結果

この記事の3行まとめ

  • 🐢 毎回 0 からループすると、Tickごとに全過去データを再計算することになる
  • prev_calculated を使った差分更新なら、計算は最新の1〜2本だけで済む
  • 🛑 「重いインジケータ」の正体は、このループ処理の書き方ひとつにあるかもしれない

はじめに

この記事は、「とあるMetaTraderの備忘秘録」様のブログ記事を検証・紹介するシリーズの第14弾です。

自作インジケータを作ってみたら「MT5がカクカクする」「CPU使用率が跳ね上がる」という経験はありませんか? もしかすると、既に計算し終わった過去のデータを、何度も何度も計算し直しているからかもしれません。

元ネタ

IndicatorCounted() の深層
https://fai-fx.hatenadiary.org/entry/20101014/1287019183
(2010年10月14日 公開)


差分更新の仕組み

インジケータ関数 OnCalculate には、prev_calculated(前回計算済みバー数)という引数があります。これを使うかどうかが運命の分かれ道です。

❌ ダメな書き方(全期間再計算)

MQL
// 毎回0から最後までループ
for(int i = 0; i < rates_total; i++)
{
    Buffer[i] = ...;
}

これだと、新しいTickが来るたびに 過去数万本のバー 全てに対して計算処理が走ります。無駄です。

✅ 正しい書き方(差分更新)

MQL
int limit;
if(prev_calculated == 0)
    limit = 0; // 初回は全部
else
    limit = prev_calculated - 1; // 2回目以降はラスト1本から

// 必要な部分だけループ
for(int i = limit; i < rates_total; i++)
{
    Buffer[i] = ...;
}

これなら、Tick更新時は 最新の1本だけ 計算すれば済みます。負荷は数万分の1です。


MQL5での検証コード

この負荷の違いを体感するためのインジケータを作成しました。

ファイル名: 14_Indi_SpeedTest.mq5
保存先: MQL5\Indicators
パラメータ: UseSmartCalc(true=高速 / false=激重)

```mql5 //+------------------------------------------------------------------+ //| 14_Indi_SpeedTest.mq5 | //| ※動作原理を示すための簡易版コードです | //| 完全版は mql5_codes/14_Indi_SpeedTest.mq5 を参照してください | //+------------------------------------------------------------------+

property indicator_separate_window

property indicator_buffers 1

property indicator_plots 1

input bool UseSmartCalc = false; // true=高速(差分), false=低速(全期間) input int LoadWeight = 100; // 計算負荷係数

double Buffer[];

int OnInit() { SetIndexBuffer(0, Buffer, INDICATOR_DATA); return(INIT_SUCCEEDED); }

int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { ulong start = GetMicrosecondCount();

int limit;

if(UseSmartCalc) { // スマート計算:差分更新 if(prev_calculated == 0) limit = 0; else limit = prev_calculated - 1; } else { // 非効率計算:毎回最初から limit = 0; }

// 計算ループ for(int i = limit; i < rates_total; i++) { // 少し重い計算を回す(負荷シミュレーション) double sum = 0; for(int k=0; k < LoadWeight; k++) { sum += MathSqrt(close[i] * high[i]) / (k+1); } Buffer[i] = sum; }

ulong elapsed = GetMicrosecondCount() - start;

string mode = UseSmartCalc ? "高速(差分)" : "低速(全期間)"; Comment(StringFormat("モード: %s\n計算時間: %.3f ms\n計算対象: %d 本", mode, elapsed/1000.0, rates_total - limit));

return(rates_total); }

結果イメージ

手元の環境(ストラテジーテスター・ビジュアルモード)での実測結果です:

モード 計算時間(目安)
全期間(False) 27.070 ms (毎回発生)
差分更新(True) 0.001 ms (一瞬)

「27msなら大したことない」と思うかもしれませんが、これが複数のチャート、複数のインジケータで同時に動くと、MT5全体の動作がもっさりしてきます。 一方、差分更新なら 0.001ms という驚異的な軽さです。


まとめ

ポイント 内容
prev_calculated これを使わない手はない
全期間ループ 致命的なパフォーマンス低下の原因
例外 過去データを参照して値が変わる特殊なインジケータ(ZigZagなど)は全計算が必要な場合もある

基本的には「差分更新」をテンプレートとして使いましょう。


ソースコードのダウンロード

この記事で紹介したコードをダウンロードできます。

ファイルの種類:インジケータ

保存先: MQL5/Indicators/ フォルダ
使い方: MT5のナビゲーターから「インディケータ」を展開し、チャートにドラッグ&ドロップで適用

14_Indi_SpeedTest.mq5 をダウンロード


関連記事・用語